Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>Definition.getParent().isVar() &&
aliasDefinition.getParent().hasOneChild()) {
aliasDefinition.getParent().detachFromParent();
} else {
aliasDefinition.detachFromParent();
}
}
// Collapse the scopes.
for (Node scopeCall : traversal.getScopeCalls()) {
Node expressionWithScopeCall = scopeCall.getParent();
Node scopeClosureBlock = scopeCall.getLastChild().getLastChild();
scopeClosureBlock.detachFromParent();
expressionWithScopeCall.getParent().replaceChild(
expressionWithScopeCall,
scopeClosureBlock);
NodeUtil.tryMergeBlock(scopeClosureBlock);
}
if (traversal.getAliasUsages().size() > 0 ||
traversal.getAliasDefinitionsInOrder().size() > 0 ||
traversal.getScopeCalls().size() > 0) {
compiler.reportCodeChange();
}
}
}
private abstract class AliasUsage {
final Var aliasVar;
final Node aliasReference;
AliasUsage(Var aliasVar, Node aliasReference) {
this.aliasVar = aliasVar;
this.aliasReference = aliasReference;
}
/** Checks to see if this references another alias. */
public boolean referencesOtherAlias() {
Node aliasDefinition = aliasVar.getInitialValue();
Node root = NodeUtil.getRootOfQualifiedName(aliasDefinition);
Var otherAliasVar = aliasVar.getScope().getOwnSlot(root.getString());
return otherAliasVar != null;
}
public abstract void applyAlias();
}
private class AliasedNode extends AliasUsage {
AliasedNode(Var aliasVar, Node aliasReference) {
super(aliasVar, aliasReference);
}
@Override
public void applyAlias() {
Node aliasDefinition = aliasVar.getInitialValue();
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode extends AliasUsage {
AliasedTypeNode(Var aliasVar, Node aliasReference) {
super(aliasVar, aliasReference);
}
@Override
public void applyAlias() {
Node aliasDefinition = aliasVar.getInitialValue();
String aliasName = aliasVar.getName();
String typeName = aliasReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> }
/**
* Detaches child from Node and replaces it with newChild.
*/
public void replaceChild(Node child, Node newChild) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(child);
newChild.next = child.next;
newChild.parent = this;
if (child == first) {
first = newChild;
} else {
Node prev = getChildBefore(child);
prev.next = newChild;
}
if (child == last) {
last = newChild;
}
child.next = null;
child.parent = null;
}
public void replaceChildAfter(Node prevChild, Node newChild) {
Preconditions.checkArgument(prevChild.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(prevChild);
Node child = prevChild.next;
newChild.next = child.next;
newChild.parent = this;
prevChild.next = newChild;
if (child == last) {
last = newChild;
}
child.next = null;
child.parent = null;
}
@VisibleForTesting
PropListItem lookupProperty(int propType) {
PropListItem x = propListHead;
while (x != null && propType != x.getType()) {
x = x.getNext();
}
return x;
}
/**
* Clone the properties from the provided node without copying
* the property object. The receiving node may not have any
* existing properties.
* @param other The node to clone properties from.
* @return this node.
*/
public Node clonePropsFrom(Node other) {
Preconditions.checkState(this.propListHead == null,
"Node has existing properties.");
this.propListHead = other.propListHead
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>(ORIGINALNAME_PROP));
}
if (getProp(STATIC_SOURCE_FILE) == null) {
putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE));
sourcePosition = other.sourcePosition;
}
return this;
}
/**
* Copies source file and name information from the other node to the
* entire tree rooted at this node.
* @return this
*/
// TODO(nicksantos): The semantics of this method are ill-defined. Delete it.
public Node copyInformationFromForTree(Node other) {
copyInformationFrom(other);
for (Node child = getFirstChild();
child != null; child = child.getNext()) {
child.copyInformationFromForTree(other);
}
return this;
}
/**
* Overwrite all the source information in this node with
* that of {@code other}.
*/
public Node useSourceInfoFrom(Node other) {
putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP));
putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE));
sourcePosition = other.sourcePosition;
return this;
}
public Node srcref(Node other) {
return useSourceInfoFrom(other);
}
/**
* Overwrite all the source information in this node and its subtree with
* that of {@code other}.
*/
public Node useSourceInfoFromForTree(Node other) {
useSourceInfoFrom(other);
for (Node child = getFirstChild();
child != null; child = child.getNext()) {
child.useSourceInfoFromForTree(other);
}
return this;
}
public Node srcrefTree(Node other) {
return useSourceInfoFromForTree(other);
}
/**
* Overwrite all the source information in this node with
* that of {@code other} iff the source info is missing.
*/
public Node useSourceInfoIfMissingFrom(Node other) {
if (getProp(ORIGINALNAME_PROP) == null) {
putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP));
}
if (getProp(STATIC_SOURCE_FILE) == null) {
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(var)) {
reportError(t, n, name);
} else {
initializedConstants.add(var);
}
}
}
break;
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD: {
Node lhs = n.getFirstChild();
if (lhs.isName()) {
String name = lhs.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(var)) {
reportError(t, n, name);
} else {
initializedConstants.add(var);
}
}
}
break;
}
case Token.INC:
case Token.DEC: {
Node lhs = n.getFirstChild();
if (lhs.isName()) {
String name = lhs.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
reportError(t, n, name);
}
}
break;
}
}
}
/**
* Gets whether a variable is a constant initialized to a literal value at
* the point where it is declared.
*/
private boolean isConstant(Scope.Var var) {
return var != null && var.isConst();
}
/**
* Reports a reassigned constant error.
*/
void reportError(NodeTraversal t, Node n, String name) {
compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name));
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> String>newLinkedHashMap());
}
}
}
enum RenameStrategy {
CONSISTENT,
INCONSISTENT,
MAPPED,
STABLE
}
private static interface NameSupplier {
String getName(String id, String name);
RenameStrategy getRenameStrategy();
}
private static class ObfuscatedNameSuppier implements NameSupplier {
private final NameGenerator generator;
private final Map<String, String> previousMappings;
private RenameStrategy renameStrategy;
public ObfuscatedNameSuppier(
RenameStrategy renameStrategy, BiMap<String, String> previousMappings) {
this.previousMappings = previousMappings.inverse();
this.generator =
new NameGenerator(previousMappings.keySet(), "", null);
this.renameStrategy = renameStrategy;
}
@Override
public String getName(String id, String name) {
String newName = previousMappings.get(id);
if (newName == null) {
newName = generator.generateNextName();
}
return newName;
}
@Override
public RenameStrategy getRenameStrategy() {
return renameStrategy;
}
}
private static class PseudoNameSuppier implements NameSupplier {
private int counter = 0;
private RenameStrategy renameStrategy;
public PseudoNameSuppier(RenameStrategy renameStrategy) {
this.renameStrategy = renameStrategy;
}
@Override
public String getName(String id, String name) {
if (renameStrategy == RenameStrategy.INCONSISTENT) {
return name + "$" + counter++;
}
return name + "$0";
}
@Override
public RenameStrategy getRenameStrategy() {
return renameStrategy;
}
}
private static class StableNameSupplier implements NameSupplier {
@Override
public String getName(String id, String name) {
return Base64.base64EncodeInt(name.hashCode());
}
@Override
public RenameStrategy getRenameStrategy() {
return RenameStrategy.STABLE;
}
}
private static class MappedNameSupplier implements NameSupplier {
private final RenamingMap map;
MappedNameSupplier(RenamingMap map) {
this.map = map;
}
@Override
public String getName(String id, String name) {
return map.get(name);
}
@
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> }
Node arg = n.getFirstChild().getNext();
if (arg.isString()) {
String rename = getObfuscatedName(
arg, callName, nameGenerator, arg.getString());
parent.replaceChild(n, IR.string(rename));
compiler.reportCodeChange();
} else if (arg.isObjectLit()) {
for (Node key : arg.children()) {
String rename = getObfuscatedName(
key, callName, nameGenerator, key.getString());
key.setString(rename);
// Prevent standard renaming by marking the key as quoted.
key.putBooleanProp(Node.QUOTED_PROP, true);
}
arg.detachFromParent();
parent.replaceChild(n, arg);
compiler.reportCodeChange();
}
// TODO(user): Error on id not a string or object literal.
}
private String getObfuscatedName(
Node id, String callName, NameSupplier nameGenerator, String name) {
String rename = null;
Map<String, String> idGeneratorMap = idGeneratorMaps.get(callName);
String instanceId = getIdForGeneratorNode(
nameGenerator.getRenameStrategy() != RenameStrategy.INCONSISTENT, id);
if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) {
Map<String, String> entry = consistNameMap.get(callName);
rename = entry.get(instanceId);
if (rename == null) {
rename = nameGenerator.getName(instanceId, name);
entry.put(instanceId, rename);
}
} else {
rename = nameGenerator.getName(instanceId, name);
}
idGeneratorMap.put(rename, instanceId);
return rename;
}
}
/**
* @return The serialize map of generators and their ids and their
* replacements.
*/
public String getSerializedIdMappings() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Map<String, String>> replacements :
idGeneratorMaps.entrySet()) {
if (!replacements.getValue().isEmpty()) {
sb.append("[");
sb.append(replacements.getKey());
sb.append("]\n\n");
for (Map.Entry<String, String> replacement :
replacements.getValue().entrySet()) {
sb.append
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>Node(n, parent);
break;
case Token.SCRIPT:
visitScriptNode(t);
break;
case Token.NEW:
visitNewNode(t, n);
}
}
private void visitScriptNode(NodeTraversal t) {
Set<String> classNames = Sets.newHashSet();
for (Node node : newNodes) {
String className = node.getFirstChild().getQualifiedName();
String outermostClassName = getOutermostClassName(className);
boolean notProvidedByConstructors =
(constructors == null || !constructors.contains(className));
boolean notProvidedByRequires =
(requires == null || (!requires.contains(className)
&& !requires.contains(outermostClassName)));
if (notProvidedByConstructors && notProvidedByRequires
&& !classNames.contains(className)) {
compiler.report(
t.makeError(node, level, MISSING_REQUIRE_WARNING, className));
classNames.add(className);
}
}
// for the next script, if there is one, we don't want the new, ctor, and
// require nodes to spill over.
this.newNodes.clear();
this.requires.clear();
this.constructors.clear();
}
private void visitCallNode(Node n, Node parent) {
String required = codingConvention.extractClassNameIfRequire(n, parent);
if (required != null) {
requires.add(required);
}
}
private void visitNewNode(NodeTraversal t, Node n) {
Node qNameNode = n.getFirstChild();
// If the ctor is something other than a qualified name, ignore it.
if (!qNameNode.isQualifiedName()) {
return;
}
// Grab the root ctor namespace.
Node nameNode = qNameNode;
for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {}
// We only consider programmer-defined constructors that are
// global variables, or are defined on global variables.
if (!nameNode.isName()) {
return;
}
String name = nameNode.getString();
Scope.Var var = t.getScope().getVar(name);
if (var == null || var.isLocal() || var.isExtern()) {
return;
}
newNodes.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>CleaupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new FieldCleanupPass(compiler);
}
};
final PassFactory scopeCleanupPassFactory =
new HotSwapPassFactory("ScopeCleanupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new MemoizedScopeCleanupPass(compiler);
}
};
final PassFactory globalVarRefCleanupPassFactory =
new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) {
@Override
protected HotSwapCompilerPass create(
AbstractCompiler compiler) {
return new GlobalVarRefCleanupPass(compiler);
}
};
/**
* A CleanupPass implementation that will remove stored scopes from the
* MemoizedScopeCreator of the compiler instance for a the hot swapped script.
* <p>
* This pass will also clear out Source Nodes of Function Types declared on
* Vars tracked by MemoizedScopeCreator
*/
static class MemoizedScopeCleanupPass implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
public MemoizedScopeCleanupPass(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
ScopeCreator creator = compiler.getTypedScopeCreator();
if (creator instanceof MemoizedScopeCreator) {
MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator;
String newSrc = scriptRoot.getSourceFileName();
for (Var var : scopeCreator.getAllSymbols()) {
JSType type = var.getType();
if (type != null) {
FunctionType fnType = type.toMaybeFunctionType();
if (fnType != null
&& newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) {
fnType.setSource(null);
}
}
}
scopeCreator.removeScopesForScript(originalRoot.getSourceFileName());
}
}
@Override
public void process(Node externs, Node root) {
// MemoizedScopeCleanupPass should not do work during process.
}
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> + Token.name(n.getType()).toLowerCase() +
"' operator is not being used.";
}
t.getCompiler().report(
t.makeError(n, level, USELESS_CODE_ERROR, msg));
// TODO(johnlenz): determine if it is necessary to
// try to protect side-effect free statements as well.
if (!NodeUtil.isStatement(n)) {
problemNodes.add(n);
}
}
}
/**
* Protect side-effect free nodes by making them parameters
* to a extern function call. This call will be removed
* after all the optimizations passes have run.
*/
private void protectSideEffects() {
if (!problemNodes.isEmpty()) {
addExtern();
for (Node n : problemNodes) {
Node name = IR.name(PROTECTOR_FN).srcref(n);
name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
Node replacement = IR.call(name).srcref(n);
replacement.putBooleanProp(Node.FREE_CALL, true);
n.getParent().replaceChild(n, replacement);
replacement.addChildToBack(n);
}
compiler.reportCodeChange();
}
}
private void addExtern() {
Node name = IR.name(PROTECTOR_FN);
name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
Node var = IR.var(name);
// Add "@noalias" so we can strip the method when AliasExternals is enabled.
JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
builder.recordNoAlias();
var.setJSDocInfo(builder.build(var));
CompilerInput input = compiler.getSynthesizedExternsInput();
input.getAstRoot(compiler).addChildrenToBack(var);
compiler.reportCodeChange();
}
/**
* Remove side-effect sync functions.
*/
static class StripProtection extends AbstractPostOrderCallback implements CompilerPass {
private final AbstractCompiler compiler;
StripProtection(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isCall()) {
Node target = n.getFirstChild();
// TODO(johnlenz): add this to the coding convention
// so we can remove goog.reflect.sinkValue as well.
if (target.isName() && target.getString().equals(PROTECTOR_FN)) {
Node expr = n.getLastChild();
n.detachChildren();
parent.replaceChild(n, expr);
}
}
}
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> new PrepareAnnotations());
}
if (root != null) {
NodeTraversal.traverse(
compiler, root, new PrepareAnnotations());
}
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& !n.isLabel()
&& !n.isSwitch()) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n, c) && !c.isBlock()) {
Node newBlock = IR.block().srcref(n);
n.replaceChild(c, newBlock);
if (!c.isEmpty()) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
implements NodeTraversal.Callback {
PrepareAnnotations() {
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isObjectLit()) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
annotateCalls(n);
break;
case Token.FUNCTION:
annotateDispatchers(n, parent);
break;
}
}
private void normalizeObjectLiteralAnnotations(Node objlit) {
Preconditions.checkState
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> expectCastIsNecessary(NodeTraversal t, Node n, JSType castType, JSType type) {
if (!reportUnnecessaryCasts) {
return;
}
if (type.isEquivalentTo(castType) ||
(type.isSubtype(castType) && !castType.isSubtype(type))) {
report(t.makeError(n, UNNECESSARY_CAST,
type.toString(), castType.toString()));
}
}
/**
* Expect that the given variable has not been declared with a type.
*
* @param sourceName The name of the source file we're in.
* @param n The node where warnings should point to.
* @param parent The parent of {@code n}.
* @param var The variable that we're checking.
* @param variableName The name of the variable.
* @param newType The type being applied to the variable. Mostly just here
* for the benefit of the warning.
* @return The variable we end up with. Most of the time, this will just
* be {@code var}, but in some rare cases we will need to declare
* a new var with new source info.
*/
Var expectUndeclaredVariable(String sourceName, CompilerInput input,
Node n, Node parent, Var var, String variableName, JSType newType) {
Var newVar = var;
boolean allowDupe = false;
if (n.isGetProp() ||
NodeUtil.isObjectLitKey(n)) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
}
JSType varType = var.getType();
// Only report duplicate declarations that have types. Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitialScope and is a
// native type. We should redeclare it at the new input site.
if (var.input == null) {
Scope s = var.getScope();
s.undeclare(var);
newVar = s.declare(variableName, n, varType, input, false);
n.setJSType(varType);
if (parent.isVar()) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.isFunction());
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().isExprResult()) ||
!newType.isEquivalentTo(varType)) {
report(JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
return newVar;
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented and correctly typed.
*/
void expectAllInterfaceProperties(NodeTraversal t, Node n,
FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
expectInterfaceProperty(t, n, instance, implemented, prop);
}
}
}
}
/**
* Expect that the property in an interface that this type implements is
* implemented and correctly typed.
*/
private void expectInterfaceProperty(NodeTraversal t, Node n,
ObjectType instance, ObjectType implementedInterface, String prop) {
StaticSlot<JSType> propSlot = instance.getSlot(prop);
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
overrideDefines(collectDefines(root, namespace));
}
private void overrideDefines(Map<String, DefineInfo> allDefines) {
boolean changed = false;
for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) {
String defineName = def.getKey();
DefineInfo info = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Only defines of literal number, string, or boolean are supported.
*/
private boolean isValidDefineType(JSTypeExpression expression) {
JSType type = expression.evaluate(null, compiler.getTypeRegistry());
return !type.isUnknownType() && type.isSubtype(
compiler.getTypeRegistry().getNativeType(
JSTypeNative.NUMBER_STRING_BOOLEAN));
}
/**
* Finds all defines, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
} else if (processDefineAssignment(t, fullName, val, valParent)) {
// remove the assignment so that the variable is still declared,
// but no longer assigned to a value, e.g.,
// DEF_FOO = 5; // becomes "5;"
// We can't remove the ASSIGN/VAR when we're still visiting its
// children, so we'll have to come back later to remove it.
refInfo.name.removeRef(ref);
lvalueToRemoveLater = valParent;
}
break;
default:
if (t.inGlobalScope()) {
// Treat this as a reference to a define in the global scope.
// After this point, the define must not be reassigned,
// or it's an error.
DefineInfo info = assignableDefines.get(fullName);
if (info != null) {
setDefineInfoNotAssignable(info, t);
assignableDefines.remove(fullName);
}
}
break;
}
}
if (!t.inGlobalScope() &&
n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
// warn about @define annotations in local scopes
compiler.report(
t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
}
if (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.isAssign()) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.isName());
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.isCall()) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
// are
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
import com.google.common.base.Preconditions;
import java.util.List;
/**
* An AST construction helper class
* @author johnlenz@google.com (John Lenz)
*/
public class IR {
private IR() {}
public static Node empty() {
return new Node(Token.EMPTY);
}
public static Node function(Node name, Node params, Node body) {
Preconditions.checkState(name.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>));
block.addChildToBack(stmt);
}
return block;
}
public static Node script(List<Node> stmts) {
Node paramList = script();
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatementNoReturn(stmt));
paramList.addChildToBack(stmt);
}
return paramList;
}
public static Node var(Node name, Node value) {
Preconditions.checkState(name.isName() && !name.hasChildren());
Preconditions.checkState(mayBeExpression(value));
name.addChildToFront(value);
return var(name);
}
public static Node var(Node name) {
Preconditions.checkState(name.isName());
return new Node(Token.VAR, name);
}
public static Node returnNode() {
return new Node(Token.RETURN);
}
public static Node returnNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.RETURN, expr);
}
public static Node throwNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.THROW, expr);
}
public static Node exprResult(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.EXPR_RESULT, expr);
}
public static Node ifNode(Node cond, Node then) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
return new Node(Token.IF, cond, then);
}
public static Node ifNode(Node cond, Node then, Node elseNode) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
Preconditions.checkState(elseNode.isBlock());
return new Node(Token.IF, cond, then, elseNode);
}
public static Node doNode(Node body, Node cond) {
Preconditions.checkState(body.isBlock());
Preconditions.checkState(mayBeExpression(cond));
return new Node(Token.DO, body, cond);
}
public static Node forIn(Node target, Node cond, Node body) {
Preconditions.checkState(target.isVar() ||
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue()));
} else if (value instanceof Integer) {
map.put(name, IR.number(((Integer) value).intValue()));
} else if (value instanceof Double) {
map.put(name, IR.number(((Double) value).doubleValue()));
} else {
Preconditions.checkState(value instanceof String);
map.put(name, IR.string((String) value));
}
}
return map;
}
/**
* Sets the value of the {@code @define} variable in JS
* to a boolean literal.
*/
public void setDefineToBooleanLiteral(String defineName, boolean value) {
defineReplacements.put(defineName, new Boolean(value));
}
/**
* Sets the value of the {@code @define} variable in JS to a
* String literal.
*/
public void setDefineToStringLiteral(String defineName, String value) {
defineReplacements.put(defineName, value);
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToNumberLiteral(String defineName, int value) {
defineReplacements.put(defineName, new Integer(value));
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToDoubleLiteral(String defineName, double value) {
defineReplacements.put(defineName, new Double(value));
}
/**
* Sets the value of the tweak in JS
* to a boolean literal.
*/
public void setTweakToBooleanLiteral(String tweakId, boolean value) {
tweakReplacements.put(tweakId, new Boolean(value));
}
/**
* Sets the value of the tweak in JS to a
* String literal.
*/
public void setTweakToStringLiteral(String tweakId, String value) {
tweakReplacements.put(tweakId, value);
}
/**
* Sets the value of the tweak in JS to a
* number literal.
*/
public void setTweakToNumberLiteral(String tweakId, int value) {
tweakReplacements.put(tweakId, new Integer(value));
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>PostOrderCallback {
private final Set<String> exposedProperties = Sets.newHashSet();
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isExprAssign(n)) {
Node assign = n.getFirstChild();
Node lhs = assign.getFirstChild();
if (lhs.isGetProp() && isMarkedExpose(assign)) {
exposedProperties.add(lhs.getLastChild().getString());
}
} else if (n.isStringKey() && isMarkedExpose(n)) {
exposedProperties.add(n.getString());
} else if (n.isGetProp() && n.getParent().isExprResult()
&& isMarkedExpose(n)) {
exposedProperties.add(n.getLastChild().getString());
}
}
private boolean isMarkedExpose(Node n) {
JSDocInfo info = n.getJSDocInfo();
return info != null && info.isExpose();
}
}
/**
* Rewrite all exposed properties in [] form.
*/
private class RewriteExposedProperties
extends AbstractPostOrderCallback {
private final Set<String> exposedProperties;
RewriteExposedProperties(Set<String> exposedProperties) {
this.exposedProperties = exposedProperties;
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isGetProp()) {
String propName = n.getLastChild().getString();
if (exposedProperties.contains(propName)) {
Node obj = n.removeFirstChild();
Node prop = n.removeFirstChild();
n.getParent().replaceChild(n, IR.getelem(obj, prop));
compiler.reportCodeChange();
}
} else if (n.isStringKey()) {
String propName = n.getString();
if (exposedProperties.contains(propName)) {
n.setQuotedString();
compiler.reportCodeChange();
}
}
}
}
/**
* Propagate constant annotations over the Var graph.
*/
static class PropagateConstantAnnotationsOverVars
extends AbstractPostOrderCallback
implements CompilerPass {
private final AbstractCompiler compiler;
private final boolean assertOnChange;
PropagateConstantAnnotationsOverVars(
AbstractCompiler compiler, boolean forbidChanges) {
this.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>compiler = compiler;
this.assertOnChange = forbidChanges;
}
@Override
public void process(Node externs, Node root) {
new NodeTraversal(compiler, this).traverseRoots(externs, root);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Note: Constant properties annotations are not propagated.
if (n.isName()) {
if (n.getString().isEmpty()) {
return;
}
JSDocInfo info = null;
// Find the JSDocInfo for a top-level variable.
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
boolean shouldBeConstant =
(info != null && info.isConstant()) ||
NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), n, parent);
boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (shouldBeConstant && !isMarkedConstant) {
if (assertOnChange) {
String name = n.getString();
throw new IllegalStateException(
"Unexpected const change.\n" +
" name: "+ name + "\n" +
" parent:" + n.getParent().toStringTree());
}
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
}
}
/**
* Walk the AST tree and verify that constant names are used consistently.
*/
static class VerifyConstants extends AbstractPostOrderCallback
implements CompilerPass {
final private AbstractCompiler compiler;
final private boolean checkUserDeclarations;
VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) {
this.compiler = compiler;
this.checkUserDeclarations = checkUserDeclarations;
}
@Override
public void process(Node externs, Node root) {
Node externsAndJs = root.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(externsAndJs.hasChild(externs));
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
private Map<String, Boolean> constantMap = Maps.newHashMap();
@Override
public void visit(NodeTraversal t, Node n
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>, Node parent) {
if (n.isName()) {
String name = n.getString();
if (n.getString().isEmpty()) {
return;
}
boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (checkUserDeclarations) {
boolean expectedConst = false;
CodingConvention convention = compiler.getCodingConvention();
if (NodeUtil.isConstantName(n)
|| NodeUtil.isConstantByConvention(convention, n, parent)) {
expectedConst = true;
} else {
expectedConst = false;
JSDocInfo info = null;
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
if (info != null && info.isConstant()) {
expectedConst = true;
} else {
expectedConst = false;
}
}
if (expectedConst) {
Preconditions.checkState(expectedConst == isConst,
"The name %s is not annotated as constant.", name);
} else {
Preconditions.checkState(expectedConst == isConst,
"The name %s should not be annotated as constant.", name);
}
}
Boolean value = constantMap.get(name);
if (value == null) {
constantMap.put(name, isConst);
} else {
Preconditions.checkState(value.booleanValue() == isConst,
"The name %s is not consistently annotated as constant.", name);
}
}
}
}
/**
* Simplify the AST:
* - VAR declarations split, so they represent exactly one child
* declaration.
* - WHILEs are converted to FORs
* - FOR loop are initializers are moved out of the FOR structure
* - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are
* moved into a block.
* - Add constant annotations based on coding convention.
*/
static class NormalizeStatements implements Callback {
private final AbstractCompiler compiler;
private final boolean assertOnChange;
NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) {
this.compiler = compiler;
this.assertOnChange = assertOnChange;
}
private void reportCodeChange(String changeDescription)
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> {
if (assertOnChange) {
throw new IllegalStateException(
"Normalize constraints violated:\n" + changeDescription);
}
compiler.reportCodeChange();
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(n);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
Node empty = IR.empty();
empty.copyInformationFrom(n);
n.addChildBefore(empty, expr);
n.addChildAfter(empty.cloneNode(), expr);
reportCodeChange("WHILE node");
}
break;
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
case Token.NAME:
case Token.STRING:
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) {
annotateConstantsByConvention(n, parent);
}
break;
case Token.CAST:
parent.replaceChild(n, n.removeFirstChild());
break;
}
}
/**
* Mark names and properties that are constants by convention.
*/
private void annotateConstantsByConvention(Node n, Node parent) {
Preconditions.checkState(
n.isName()
|| n.isString()
|| n.isStringKey()
|| n.isGetterDef()
|| n.isSetterDef());
// There are only two cases where a string token
// may be a variable reference: The right side of a GETPROP
// or an OBJECTLIT key.
boolean isObjLitKey = NodeUtil.isObjectLitKey(n);
boolean isProperty = isObjLitKey ||
(parent.isGetProp() &&
parent.getLastChild() == n);
if (n.isName() || isProperty) {
boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (!isMarkedConstant &&
NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), n
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>, parent)) {
if (assertOnChange) {
String name = n.getString();
throw new IllegalStateException(
"Unexpected const change.\n" +
" name: "+ name + "\n" +
" parent:" + n.getParent().toStringTree());
}
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.isFunction());
if (!NodeUtil.isFunctionExpression(n)
&& !NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
private void rewriteFunctionDeclaration(Node n) {
// Prepare a spot for the function.
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = IR.var(fnNameNode).srcref(n);
// Prepare the function
oldNameNode.setString("");
// Move the function
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(Node n) {
if (n.isLabel()) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.isLabel()) {
extractForInitializer(n, null, null);
}
// Only inspect
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> the children of SCRIPTs, BLOCKs, as all these
// are the only legal place for VARs.
if (NodeUtil.isStatementBlock(n)) {
splitVarDeclarations(n);
}
if (n.isFunction()) {
moveNamedFunctions(n.getLastChild());
}
}
// TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are
// fixed.
/**
* Limit the number of special cases where LABELs need to be handled. Only
* BLOCK and loops are allowed to be labeled. Loop labels must remain in
* place as the named continues are not allowed for labeled blocks.
*/
private void normalizeLabels(Node n) {
Preconditions.checkArgument(n.isLabel());
Node last = n.getLastChild();
switch (last.getType()) {
case Token.LABEL:
case Token.BLOCK:
case Token.FOR:
case Token.WHILE:
case Token.DO:
return;
default:
Node block = IR.block();
block.copyInformationFrom(last);
n.replaceChild(last, block);
block.addChildToFront(last);
reportCodeChange("LABEL normalization");
return;
}
}
/**
* Bring the initializers out of FOR loops. These need to be placed
* before any associated LABEL nodes. This needs to be done from the top
* level label first so this is called as a pre-order callback (from
* shouldTraverse).
*
* @param n The node to inspect.
* @param before The node to insert the initializer before.
* @param beforeParent The parent of the node before which the initializer
* will be inserted.
*/
private void extractForInitializer(
Node n, Node before, Node beforeParent) {
for (Node next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
Node insertBefore = (before == null) ? c : before;
Node insertBeforeParent = (before == null) ? n : beforeParent;
switch (c.getType()) {
case Token.LABEL:
extractForInitializer(c, insertBefore, insertBeforeParent);
break;
case Token.FOR:
if (NodeUtil.isForIn(c)) {
Node
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> first = c.getFirstChild();
if (first.isVar()) {
// Transform:
// for (var a = 1 in b) {}
// to:
// var a = 1; for (a in b) {};
Node newStatement = first;
// Clone just the node, to remove any initialization.
Node name = newStatement.getFirstChild().cloneNode();
first.getParent().replaceChild(first, name);
insertBeforeParent.addChildBefore(newStatement, insertBefore);
reportCodeChange("FOR-IN var declaration");
}
} else if (!c.getFirstChild().isEmpty()) {
Node init = c.getFirstChild();
Node empty = IR.empty();
empty.copyInformationFrom(c);
c.replaceChild(init, empty);
Node newStatement;
// Only VAR statements, and expressions are allowed,
// but are handled differently.
if (init.isVar()) {
newStatement = init;
} else {
newStatement = NodeUtil.newExpr(init);
}
insertBeforeParent.addChildBefore(newStatement, insertBefore);
reportCodeChange("FOR initializer");
}
break;
}
}
}
/**
* Split a var node such as:
* var a, b;
* into individual statements:
* var a;
* var b;
* @param n The whose children we should inspect.
*/
private void splitVarDeclarations(Node n) {
for (Node next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
if (c.isVar()) {
if (assertOnChange && !c.hasChildren()) {
throw new IllegalStateException("Empty VAR node.");
}
while (c.getFirstChild() != c.getLastChild()) {
Node name = c.getFirstChild();
c.removeChild(name);
Node newVar = IR.var(name).srcref(n);
n.addChildBefore(newVar, c);
reportCodeChange("VAR with multiple children");
}
}
}
}
/**
* Move all the functions that are valid at the execution of the first
* statement of the function to the beginning of the function definition.
*/
private void moveNamedFunctions(Node functionBody) {
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> Preconditions.checkState(
functionBody.getParent().isFunction());
Node previous = null;
Node current = functionBody.getFirstChild();
// Skip any declarations at the beginning of the function body, they
// are already in the right place.
while (current != null && NodeUtil.isFunctionDeclaration(current)) {
previous = current;
current = current.getNext();
}
// Find any remaining declarations and move them.
Node insertAfter = previous;
while (current != null) {
// Save off the next node as the current node maybe removed.
Node next = current.getNext();
if (NodeUtil.isFunctionDeclaration(current)) {
// Remove the declaration from the body.
Preconditions.checkNotNull(previous);
functionBody.removeChildAfter(previous);
// Read the function at the top of the function body (after any
// previous declarations).
insertAfter = addToFront(functionBody, current, insertAfter);
reportCodeChange("Move function declaration not at top of function");
} else {
// Update the previous only if the current node hasn't been moved.
previous = current;
}
current = next;
}
}
/**
* @param after The child node to insert the newChild after, or null if
* newChild should be added to the front of parent's child list.
* @return The inserted child node.
*/
private Node addToFront(Node parent, Node newChild, Node after) {
if (after == null) {
parent.addChildToFront(newChild);
} else {
parent.addChildAfter(newChild, after);
}
return newChild;
}
}
/**
* Remove duplicate VAR declarations.
*/
private void removeDuplicateDeclarations(Node externs, Node root) {
Callback tickler = new ScopeTicklingCallback();
ScopeCreator scopeCreator = new SyntacticScopeCreator(
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverseRoots(externs, root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler implements
SyntacticScopeCreator.RedeclarationHandler {
private Set<Var> hasOkDuplicateDeclaration = Sets.newHashSet();
/**
* Remove duplicate VAR declarations encountered discovered during
* scope creation.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>parent.hasOneChild());
replaceVarWithAssignment(n, parent, parent.getParent());
}
}
/**
* Remove the parent VAR. There are three cases that need to be handled:
* 1) "var a = b;" which is replaced with "a = b"
* 2) "label:var a;" which is replaced with "label:;". Ideally, the
* label itself would be removed but that is not possible in the
* context in which "onRedeclaration" is called.
* 3) "for (var a in b) ..." which is replaced with "for (a in b)..."
* Cases we don't need to handle are VARs with multiple children,
* which have already been split into separate declarations, so there
* is no need to handle that here, and "for (var a;;);", which has
* been moved out of the loop.
* The result of this is that in each case the parent node is replaced
* which is generally dangerous in a traversal but is fine here with
* the scope creator, as the next node of interest is the parent's
* next sibling.
*/
private void replaceVarWithAssignment(Node n, Node parent, Node gramps) {
if (n.hasChildren()) {
// The * is being initialize, preserve the new value.
parent.removeChild(n);
// Convert "var name = value" to "name = value"
Node value = n.getFirstChild();
n.removeChild(value);
Node replacement = IR.assign(n, value);
replacement.copyInformationFrom(parent);
gramps.replaceChild(parent, NodeUtil.newExpr(replacement));
} else {
// It is an empty reference remove it.
if (NodeUtil.isStatementBlock(gramps)) {
gramps.removeChild(parent);
} else if (gramps.isFor()) {
// This is the "for (var a in b)..." case. We don't need to worry
// about initializers in "for (var a;;)..." as those are moved out
// as part of the other normalizations.
parent.removeChild(n);
gramps.replaceChild(parent, n);
} else {
Preconditions
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>, mark it as a duplicate.
if ((parent.isVar() ||
NodeUtil.isFunctionDeclaration(parent)) &&
varsToDeclareInExterns.contains(varName)) {
createSynthesizedExternVar(varName);
n.addSuppression("duplicate");
}
// Check that the var has been declared.
Scope scope = t.getScope();
Scope.Var var = scope.getVar(varName);
if (var == null) {
if (NodeUtil.isFunctionExpression(parent)) {
// e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the
// current scope.
} else {
boolean isArguments = scope.isLocal() && ARGUMENTS.equals(varName);
// The extern checks are stricter, don't report a second error.
if (!isArguments && !(strictExternCheck && t.getInput().isExtern())) {
t.report(n, UNDEFINED_VAR_ERROR, varName);
}
if (sanityCheck) {
throw new IllegalStateException("Unexpected variable " + varName);
} else {
createSynthesizedExternVar(varName);
scope.getGlobalScope().declare(varName, n,
null, compiler.getSynthesizedExternsInput());
}
}
return;
}
CompilerInput currInput = t.getInput();
CompilerInput varInput = var.input;
if (currInput == varInput || currInput == null || varInput == null) {
// The variable was defined in the same file. This is fine.
return;
}
// Check module dependencies.
JSModule currModule = currInput.getModule();
JSModule varModule = varInput.getModule();
JSModuleGraph moduleGraph = compiler.getModuleGraph();
if (!sanityCheck &&
varModule != currModule && varModule != null && currModule != null) {
if (moduleGraph.dependsOn(currModule, varModule)) {
// The module dependency was properly declared.
} else {
if (scope.isGlobal()) {
if (moduleGraph.dependsOn(varModule, currModule)) {
// The variable reference violates a declared module dependency.
t.report(n, VIOLATED_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>);
} else {
// The variable reference is between two modules that have no
// dependency relationship. This should probably be considered an
// error, but just issue a warning for now.
t.report(n, MISSING_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
} else {
t.report(n, STRICT_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
}
}
}
/**
* Create a new variable in a synthetic script. This will prevent
* subsequent compiler passes from crashing.
*/
private void createSynthesizedExternVar(String varName) {
Node nameNode = IR.name(varName);
// Mark the variable as constant if it matches the coding convention
// for constant vars.
// NOTE(nicksantos): honestly, I'm not sure how much this matters.
// AFAIK, all people who use the CONST coding convention also
// compile with undeclaredVars as errors. We have some test
// cases for this configuration though, and it makes them happier.
if (compiler.getCodingConvention().isConstant(varName)) {
nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
getSynthesizedExternsRoot().addChildToBack(
IR.var(nameNode));
varsToDeclareInExterns.remove(varName);
compiler.reportCodeChange();
}
/**
* A check for name references in the externs inputs. These used to prevent
* a variable from getting renamed, but no longer have any effect.
*/
private class NameRefInExternsCheck extends AbstractPostOrderCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
switch (parent.getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.PARAM_LIST:
// These are okay.
break;
case Token.GETPROP:
if (n == parent.getFirstChild()) {
Scope scope = t.getScope();
Scope.Var var = scope.getVar(n.getString());
if (var == null) {
t.report(n, UNDEFINED_EXTERN_VAR_
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.keySet();
}
@Override
public Scope getScope(Var var) {
return var.scope;
}
/**
* Gets the reference collection for the given variable.
*/
@Override
public ReferenceCollection getReferences(Var v) {
return referenceMap.get(v);
}
/**
* For each node, update the block stack and reference collection
* as appropriate.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
Var v;
if (n.getString().equals("arguments")) {
v = t.getScope().getArgumentsVar();
} else {
v = t.getScope().getVar(n.getString());
}
if (v != null && varFilter.apply(v)) {
addReference(v, new Reference(n, t, blockStack.peek()));
}
}
if (isBlockBoundary(n, parent)) {
blockStack.pop();
}
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getScope().getRootNode();
BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek();
blockStack.push(new BasicBlock(parent, n));
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void exitScope(NodeTraversal t) {
blockStack.pop();
if (t.getScope().isGlobal()) {
// Update global scope reference lists when we are done with it.
compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot());
behavior.afterExitScope(t, compiler.getGlobalVarReferences());
} else {
behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap));
}
}
/**
* Updates block stack.
*/
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// If node is a new basic block, put on basic block stack
if (isBlockBoundary(n, parent)) {
blockStack.push(new BasicBlock(blockStack.peek(), n));
}
return true;
}
/**
* @return true if
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> this node marks the start of a new basic block
*/
private static boolean isBlockBoundary(Node n, Node parent) {
if (parent != null) {
switch (parent.getType()) {
case Token.DO:
case Token.FOR:
case Token.TRY:
case Token.WHILE:
case Token.WITH:
// NOTE: TRY has up to 3 child blocks:
// TRY
// BLOCK
// BLOCK
// CATCH
// BLOCK
// Note that there is an explicit CATCH token but no explicit
// FINALLY token. For simplicity, we consider each BLOCK
// a separate basic BLOCK.
return true;
case Token.AND:
case Token.HOOK:
case Token.IF:
case Token.OR:
// The first child of a conditional is not a boundary,
// but all the rest of the children are.
return n != parent.getFirstChild();
}
}
return n.isCase();
}
private void addReference(Var v, Reference reference) {
// Create collection if none already
ReferenceCollection referenceInfo = referenceMap.get(v);
if (referenceInfo == null) {
referenceInfo = new ReferenceCollection();
referenceMap.put(v, referenceInfo);
}
// Add this particular reference
referenceInfo.add(reference);
}
interface ReferenceMap {
ReferenceCollection getReferences(Var var);
}
private static class ReferenceMapWrapper implements ReferenceMap {
private final Map<Var, ReferenceCollection> referenceMap;
public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) {
this.referenceMap = referenceMap;
}
@Override
public ReferenceCollection getReferences(Var var) {
return referenceMap.get(var);
}
}
/**
* Way for callers to add specific behavior during traversal that
* utilizes the built-up reference information.
*/
interface Behavior {
/**
* Called after we finish with a scope.
*/
void afterExitScope(NodeTraversal t, ReferenceMap referenceMap);
}
static final Behavior DO_NOTHING_BEHAVIOR = new Behavior() {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {}
};
/**
* A collection of references. Can be subclassed to apply checks or
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.getScope(), t.getInput().getInputId());
}
/**
* Creates a variable reference in a given script file name, used in tests.
*
* @return The created reference.
*/
@VisibleForTesting
static Reference createRefForTest(CompilerInput input) {
return new Reference(new Node(Token.NAME), null, null,
input.getInputId());
}
private Reference(Node nameNode,
BasicBlock basicBlock, Scope scope, InputId inputId) {
this.nameNode = nameNode;
this.basicBlock = basicBlock;
this.scope = scope;
this.inputId = inputId;
this.sourceFile = nameNode.getStaticSourceFile();
}
/**
* Makes a copy of the current reference using a new Scope instance.
*/
Reference cloneWithNewScope(Scope newScope) {
return new Reference(nameNode, basicBlock, newScope, inputId);
}
@Override
public Var getSymbol() {
return scope.getVar(nameNode.getString());
}
@Override
public Node getNode() {
return nameNode;
}
public InputId getInputId() {
return inputId;
}
@Override
public StaticSourceFile getSourceFile() {
return sourceFile;
}
boolean isDeclaration() {
Node parent = getParent();
Node grandparent = parent.getParent();
return DECLARATION_PARENTS.contains(parent.getType()) ||
parent.isParamList() &&
grandparent.isFunction();
}
boolean isVarDeclaration() {
return getParent().isVar();
}
boolean isHoistedFunction() {
return NodeUtil.isHoistedFunctionDeclaration(getParent());
}
/**
* Determines whether the variable is initialized at the declaration.
*/
boolean isInitializingDeclaration() {
// VAR is the only type of variable declaration that may not initialize
// its variable. Catch blocks, named functions, and parameters all do.
return isDeclaration() &&
!getParent().isVar() ||
nameNode.getFirstChild() != null;
}
/**
* @return For an assignment, variable declaration, or function declaration
* return the assigned value, otherwise null.
*/
Node getAssignedValue() {
Node parent = getParent();
return (parent.isFunction())
? parent : NodeUtil.getAssignedValue
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>(nameNode);
}
BasicBlock getBasicBlock() {
return basicBlock;
}
Node getParent() {
return getNode().getParent();
}
Node getGrandparent() {
Node parent = getParent();
return parent == null ? null : parent.getParent();
}
private static boolean isLhsOfForInExpression(Node n) {
Node parent = n.getParent();
if (parent.isVar()) {
return isLhsOfForInExpression(parent);
}
return NodeUtil.isForIn(parent) && parent.getFirstChild() == n;
}
boolean isSimpleAssignmentToName() {
Node parent = getParent();
return parent.isAssign()
&& parent.getFirstChild() == nameNode;
}
boolean isLvalue() {
Node parent = getParent();
int parentType = parent.getType();
return (parentType == Token.VAR && nameNode.getFirstChild() != null)
|| parentType == Token.INC
|| parentType == Token.DEC
|| (NodeUtil.isAssignmentOp(parent)
&& parent.getFirstChild() == nameNode)
|| isLhsOfForInExpression(nameNode);
}
Scope getScope() {
return scope;
}
}
/**
* Represents a section of code that is uninterrupted by control structures
* (conditional or iterative logic).
*/
static final class BasicBlock {
private final BasicBlock parent;
/**
* Determines whether the block may not be part of the normal control flow,
* but instead "hoisted" to the top of the scope.
*/
private final boolean isHoisted;
/**
* Whether this block denotes a function scope.
*/
private final boolean isFunction;
/**
* Whether this block denotes a loop.
*/
private final boolean isLoop;
/**
* Creates a new block.
* @param parent The containing block.
* @param root The root node of the block.
*/
BasicBlock(BasicBlock parent, Node root) {
this.parent = parent;
// only named functions may be hoisted.
this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root);
this.isFunction = root.isFunction();
if (root.getParent() != null) {
int pType = root.getParent().getType
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
}
BasicBlock getParent() {
return parent;
}
/**
* Determines whether this block is equivalent to the very first block that
* is created when reference collection traversal enters global scope. Note
* that when traversing a single script in a hot-swap fashion a new instance
* of {@code BasicBlock} is created.
*
* @return true if this is global scope block.
*/
boolean isGlobalScopeBlock() {
return getParent() == null;
}
/**
* Determines whether this block is guaranteed to begin executing before
* the given block does.
*/
boolean provablyExecutesBefore(BasicBlock thatBlock) {
// If thatBlock is a descendant of this block, and there are no hoisted
// blocks between them, then this block must start before thatBlock.
BasicBlock currentBlock;
for (currentBlock = thatBlock;
currentBlock != null && currentBlock != this;
currentBlock = currentBlock.getParent()) {
if (currentBlock.isHoisted) {
return false;
}
}
if (currentBlock == this) {
return true;
}
if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) {
return true;
}
return false;
}
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
expectedType, object, pname);
checkPropertyInheritanceOnGetpropAssign(
t, assign, object, pname, info, expectedType);
return;
}
}
}
// If we couldn't get the property type with normal object property
// lookups, then check inheritance anyway with the unknown type.
checkPropertyInheritanceOnGetpropAssign(
t, assign, object, pname, info, getNativeType(UNKNOWN_TYPE));
}
// Check qualified name sets to 'object' and 'object.property'.
// This can sometimes handle cases when the type of 'object' is not known.
// e.g.,
// var obj = createUnknownType();
// /** @type {number} */ obj.foo = true;
JSType leftType = getJSType(lvalue);
if (lvalue.isQualifiedName()) {
// variable with inferred type case
Var var = t.getScope().getVar(lvalue.getQualifiedName());
if (var != null) {
if (var.isTypeInferred()) {
return;
}
if (NodeUtil.getRootOfQualifiedName(lvalue).isThis() &&
t.getScope() != var.getScope()) {
// Don't look at "this.foo" variables from other scopes.
return;
}
if (var.getType() != null) {
leftType = var.getType();
}
}
}
// Fall through case for arbitrary LHS and arbitrary RHS.
Node rightChild = assign.getLastChild();
JSType rightType = getJSType(rightChild);
if (validator.expectCanAssignTo(
t, assign, rightType, leftType, "assignment")) {
ensureTyped(t, assign, rightType);
} else {
ensureTyped(t, assign);
}
}
private void checkPropCreation(NodeTraversal t, Node lvalue) {
if (lvalue.isGetProp()) {
JSType objType = getJSType(lvalue.getFirstChild());
Node prop = lvalue.getLastChild();
if (objType.isStruct() && !objType.hasProperty(prop.getString())) {
report(t, prop, ILLEGAL_PROPERTY_CREATION);
}
}
}
private void checkPropertyInheritanceOnGetpropAssign(
NodeTraversal t
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>,
abstractMethodMessage));
}
if (assign.getLastChild().isFunction()
&& !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) {
compiler.report(
t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY,
abstractMethodName));
}
}
/**
* Visits a NAME node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
* @return whether the node is typeable or not
*/
boolean visitName(NodeTraversal t, Node n, Node parent) {
// At this stage, we need to determine whether this is a leaf
// node in an expression (which therefore needs to have a type
// assigned for it) versus some other decorative node that we
// can safely ignore. Function names, arguments (children of LP nodes) and
// variable declarations are ignored.
// TODO(user): remove this short-circuiting in favor of a
// pre order traversal of the FUNCTION, CATCH, LP and VAR nodes.
int parentNodeType = parent.getType();
if (parentNodeType == Token.FUNCTION ||
parentNodeType == Token.CATCH ||
parentNodeType == Token.PARAM_LIST ||
parentNodeType == Token.VAR) {
return false;
}
JSType type = n.getJSType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
Var var = t.getScope().getVar(n.getString());
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
type = varType;
}
}
}
ensureTyped(t, n, type);
return true;
}
/**
* Visits a GETPROP node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of <code>n</code>
*/
private void visitGetProp(NodeTraversal t, Node n, Node parent) {
//
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> as error reporting.
* @param n The node being visited.
*/
private void visitVar(NodeTraversal t, Node n) {
// TODO(nicksantos): Fix this so that the doc info always shows up
// on the NAME node. We probably want to wait for the parser
// merge to fix this.
JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null;
for (Node name : n.children()) {
Node value = name.getFirstChild();
// A null var would indicate a bug in the scope creation logic.
Var var = t.getScope().getVar(name.getString());
if (value != null) {
JSType valueType = getJSType(value);
JSType nameType = var.getType();
nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType;
JSDocInfo info = name.getJSDocInfo();
if (info == null) {
info = varInfo;
}
checkEnumAlias(t, info, value);
if (var.isTypeInferred()) {
ensureTyped(t, name, valueType);
} else {
validator.expectCanAssignTo(
t, value, valueType, nameType, "initializing variable");
}
}
}
}
/**
* Visits a NEW node.
*/
private void visitNew(NodeTraversal t, Node n) {
Node constructor = n.getFirstChild();
JSType type = getJSType(constructor).restrictByNotNullOrUndefined();
if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) {
FunctionType fnType = type.toMaybeFunctionType();
if (fnType != null && fnType.hasInstanceType()) {
visitParameterList(t, n, fnType);
ensureTyped(t, n, fnType.getInstanceType());
} else {
ensureTyped(t, n);
}
} else {
report(t, n, NOT_A_CONSTRUCTOR);
ensureTyped(t, n);
}
}
/**
* Check whether there's any property conflict for for a particular super
* interface
* @param t The node traversal object that supplies context
* @param n The
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> function has an inline return annotation, attach it
Node newName = transformNodeWithInlineJsDoc(name);
if (isUnnamedFunction) {
// Old Rhino tagged the empty name node with the line number of the
// declaration.
newName.setLineno(functionNode.getLineno());
// TODO(bowdidge) Mark line number of paren correctly.
// Same problem as below - the left paren might not be on the
// same line as the function keyword.
int lpColumn = functionNode.getAbsolutePosition() +
functionNode.getLp();
newName.setCharno(position2charno(lpColumn));
maybeSetLengthFrom(newName, name);
}
node.addChildToBack(newName);
Node lp = newNode(Token.PARAM_LIST);
// The left paren's complicated because it's not represented by an
// AstNode, so there's nothing that has the actual line number that it
// appeared on. We know the paren has to appear on the same line as the
// function name (or else a semicolon will be inserted.) If there's no
// function name, assume the paren was on the same line as the function.
// TODO(bowdidge): Mark line number of paren correctly.
Name fnName = functionNode.getFunctionName();
if (fnName != null) {
lp.setLineno(fnName.getLineno());
} else {
lp.setLineno(functionNode.getLineno());
}
int lparenCharno = functionNode.getLp() +
functionNode.getAbsolutePosition();
lp.setCharno(position2charno(lparenCharno));
for (AstNode param : functionNode.getParams()) {
Node paramNode = transformNodeWithInlineJsDoc(param);
// When in ideMode Rhino can generate a param list with only a single
// ErrorNode. This is transformed into an EMPTY node. Drop this node in
// ideMode to keep the AST in a valid state.
if (paramNode.isName()) {
lp.addChildToBack(paramNode);
} else {
// We expect this in ideMode or when there is an error handling
// destructuring parameter assignments which aren't supported
// (an error has already been reported).
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
Preconditions.checkState(
config.isIdeMode
|| paramNode.isObjectLit()
|| paramNode.isArrayLit());
}
}
node.addChildToBack(lp);
Node bodyNode = transform(functionNode.getBody());
if (!bodyNode.isBlock()) {
// When in ideMode Rhino tries to parse some constructs the compiler
// doesn't support, repair it here. see Rhino's
// Parser#parseFunctionBodyExpr.
Preconditions.checkState(config.isIdeMode);
bodyNode = IR.block();
}
parseDirectives(bodyNode);
node.addChildToBack(bodyNode);
return node;
}
@Override
Node processIfStatement(IfStatement statementNode) {
Node node = newNode(Token.IF);
node.addChildToBack(transform(statementNode.getCondition()));
node.addChildToBack(transformBlock(statementNode.getThenPart()));
if (statementNode.getElsePart() != null) {
node.addChildToBack(transformBlock(statementNode.getElsePart()));
}
return node;
}
@Override
Node processInfixExpression(InfixExpression exprNode) {
Node n = newNode(
transformTokenType(exprNode.getType()),
transform(exprNode.getLeft()),
transform(exprNode.getRight()));
n.setLineno(exprNode.getLineno());
n.setCharno(position2charno(exprNode.getAbsolutePosition()));
maybeSetLengthFrom(n, exprNode);
return n;
}
@Override
Node processKeywordLiteral(KeywordLiteral literalNode) {
return newNode(transformTokenType(literalNode.getType()));
}
@Override
Node processLabel(Label labelNode) {
return newStringNode(Token.LABEL_NAME, labelNode.getName());
}
@Override
Node processLabeledStatement(LabeledStatement statementNode) {
Node node = newNode(Token.LABEL);
Node prev = null;
Node cur = node;
for (Label label : statementNode.getLabels()) {
if (prev != null) {
prev.addChildToBack(cur);
}
cur.addChildToBack(transform(label));
cur.setLineno(label.getLineno());
maybeSetLengthFrom(cur, label);
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>google.javascript.rhino.head.Token.CATCH:
return Token.CATCH;
case com.google.javascript.rhino.head.Token.VOID:
return Token.VOID;
case com.google.javascript.rhino.head.Token.EMPTY:
return Token.EMPTY;
case com.google.javascript.rhino.head.Token.BLOCK:
return Token.BLOCK;
case com.google.javascript.rhino.head.Token.LABEL:
return Token.LABEL;
case com.google.javascript.rhino.head.Token.EXPR_VOID:
case com.google.javascript.rhino.head.Token.EXPR_RESULT:
return Token.EXPR_RESULT;
case com.google.javascript.rhino.head.Token.SCRIPT:
return Token.SCRIPT;
case com.google.javascript.rhino.head.Token.GET:
return Token.GETTER_DEF;
case com.google.javascript.rhino.head.Token.SET:
return Token.SETTER_DEF;
case com.google.javascript.rhino.head.Token.CONST:
return Token.CONST;
case com.google.javascript.rhino.head.Token.DEBUGGER:
return Token.DEBUGGER;
}
// Token without name
throw new IllegalStateException(String.valueOf(token));
}
// Simple helper to create nodes and set the initial node properties.
private Node newNode(int type) {
return new Node(type).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1) {
return new Node(type, child1).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2) {
return new Node(type, child1, child2).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2, Node child3) {
return new Node(type, child1, child2, child3).clonePropsFrom(templateNode);
}
private Node newStringNode(String value) {
return IR.string(value).clonePropsFrom(templateNode);
}
private Node newStringNode(int type, String value) {
return Node.newString(type, value).
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>clonePropsFrom(templateNode);
}
private Node newNumberNode(Double value) {
return IR.number(value).clonePropsFrom(templateNode);
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> if (reference.isHoistedFunction()) {
blocksWithDeclarations.add(reference.getBasicBlock());
isDeclaredInScope = true;
hoistedFn = reference;
break;
} else if (NodeUtil.isFunctionDeclaration(
reference.getNode().getParent())) {
isUnhoistedNamedFunction = true;
}
}
for (Reference reference : references) {
if (reference == hoistedFn) {
continue;
}
BasicBlock basicBlock = reference.getBasicBlock();
boolean isDeclaration = reference.isDeclaration();
boolean allowDupe =
VarCheck.hasDuplicateDeclarationSuppression(
reference.getNode(), v);
if (isDeclaration && !allowDupe) {
// Look through all the declarations we've found so far, and
// check if any of them are before this block.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (declaredBlock.provablyExecutesBefore(basicBlock)) {
// TODO(johnlenz): Fix AST generating clients that so they would
// have property StaticSourceFile attached at each node. Or
// better yet, make sure the generated code never violates
// the requirement to pass aggressive var check!
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
checkLevel,
REDECLARED_VARIABLE, v.name));
break;
}
}
}
if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) {
// Only allow an unhoisted named function to be used within the
// block it is declared.
for (BasicBlock declaredBlock : blocksWithDeclarations) {
if (!declaredBlock.provablyExecutesBefore(basicBlock)) {
String filename = NodeUtil.getSourceName(reference.getNode());
compiler.report(
JSError.make(filename,
reference.getNode(),
AMBIGUOUS_FUNCTION_DECL, v.name));
break;
}
}
}
if (!isDeclaration && !isDeclaredInScope) {
// Don't check the order of refer in externs files.
if (!reference.getNode().isFromExterns()) {
// Special case to deal with var goog = goog || {}
Node grandparent = reference.getGrandparent();
if (
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>JsAst ast) {
InputId id = ast.getInputId();
Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName());
putCompilerInput(id, new CompilerInput(ast));
}
/**
* Replace a source input dynamically. Intended for incremental
* re-compilation.
*
* If the new source input doesn't parse, then keep the old input
* in the AST and return false.
*
* @return Whether the new AST was attached successfully.
*/
boolean replaceIncrementalSourceAst(JsAst ast) {
CompilerInput oldInput = getInput(ast.getInputId());
Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName());
Node newRoot = ast.getAstRoot(this);
if (newRoot == null) {
return false;
}
Node oldRoot = oldInput.getAstRoot(this);
if (oldRoot != null) {
oldRoot.getParent().replaceChild(oldRoot, newRoot);
} else {
getRoot().getLastChild().addChildToBack(newRoot);
}
CompilerInput newInput = new CompilerInput(ast);
putCompilerInput(ast.getInputId(), newInput);
JSModule module = oldInput.getModule();
if (module != null) {
module.addAfter(newInput, oldInput);
module.remove(oldInput);
}
// Verify the input id is set properly.
Preconditions.checkState(
newInput.getInputId().equals(oldInput.getInputId()));
InputId inputIdOnAst = newInput.getAstRoot(this).getInputId();
Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst));
inputs.remove(oldInput);
return true;
}
/**
* Add a new source input dynamically. Intended for incremental compilation.
* <p>
* If the new source input doesn't parse, it will not be added, and a false
* will be returned.
*
* @param ast the JS Source to add.
* @return true if the source was added successfully, false otherwise.
* @throws IllegalStateException if an input for this ast already exists.
*/
boolean addNewSourceAst(JsAst ast) {
Compiler
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> != null) {
symbolTable.addSymbolsFrom(globalNamespace);
}
ReferenceCollectingCallback refCollector =
new ReferenceCollectingCallback(
this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR);
NodeTraversal.traverse(this, getRoot(), refCollector);
symbolTable.addSymbolsFrom(refCollector);
PreprocessorSymbolTable preprocessorSymbolTable =
ensureDefaultPassConfig().getPreprocessorSymbolTable();
if (preprocessorSymbolTable != null) {
symbolTable.addSymbolsFrom(preprocessorSymbolTable);
}
symbolTable.fillNamespaceReferences();
symbolTable.fillPropertyScopes();
symbolTable.fillThisReferences(this, externsRoot, jsRoot);
symbolTable.fillPropertySymbols(this, externsRoot, jsRoot);
symbolTable.fillJSDocInfo(this, externsRoot, jsRoot);
return symbolTable;
}
@Override
public Scope getTopScope() {
return getPassConfig().getTopScope();
}
@Override
public ReverseAbstractInterpreter getReverseAbstractInterpreter() {
if (abstractInterpreter == null) {
ChainableReverseAbstractInterpreter interpreter =
new SemanticReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry());
if (options.closurePass) {
interpreter = new ClosureReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry())
.append(interpreter).getFirst();
}
abstractInterpreter = interpreter;
}
return abstractInterpreter;
}
@Override
TypeValidator getTypeValidator() {
if (typeValidator == null) {
typeValidator = new TypeValidator(this);
}
return typeValidator;
}
//------------------------------------------------------------------------
// Parsing
//------------------------------------------------------------------------
/**
* Parses the externs and main inputs.
*
* @return A synthetic root node whose two children are the externs root
* and the main root
*/
Node parseInputs() {
boolean devMode = options.devMode != DevMode.OFF;
// If old roots exist (we are parsing a second time), detach each of the
// individual file parse trees.
if (externsRoot != null) {
externsRoot.detachChildren();
}
if (jsRoot != null) {
jsRoot.detachChildren();
}
// Parse main JS sources.
jsRoot = IR.block();
jsRoot.setIsSynt
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>heticBlock(true);
externsRoot = IR.block();
externsRoot.setIsSyntheticBlock(true);
externAndJsRoot = IR.block(externsRoot, jsRoot);
externAndJsRoot.setIsSyntheticBlock(true);
if (options.tracer.isOn()) {
tracker = new PerformanceTracker(jsRoot, options.tracer);
addChangeHandler(tracker.getCodeChangeHandler());
}
Tracer tracer = newTracer(PARSING_PASS_NAME);
try {
// Parse externs sources.
for (CompilerInput input : externs) {
Node n = input.getAstRoot(this);
if (hasErrors()) {
return null;
}
externsRoot.addChildToBack(n);
}
// Modules inferred in ProcessCommonJS pass.
if (options.transformAMDToCJSModules || options.processCommonJSModules) {
processAMDAndCommonJSModules();
}
hoistExterns(externsRoot);
// Check if the sources need to be re-ordered.
boolean staleInputs = false;
if (options.dependencyOptions.needsManagement()) {
for (CompilerInput input : inputs) {
// Forward-declare all the provided types, so that they
// are not flagged even if they are dropped from the process.
for (String provide : input.getProvides()) {
getTypeRegistry().forwardDeclareType(provide);
}
}
try {
inputs =
(moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph)
.manageDependencies(options.dependencyOptions, inputs);
staleInputs = true;
} catch (CircularDependencyException e) {
report(JSError.make(
JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
} catch (MissingProvideException e) {
report(JSError.make(
MISSING_ENTRY_ERROR, e.getMessage()));
} catch (JSModuleGraph.MissingModuleException e) {
report(JSError.make(
MISSING_MODULE_ERROR, e.getMessage()));
}
// If in IDE mode, we ignore the error and keep going.
if (hasErrors()) {
return null;
}
}
hoistNoCompileFiles();
if (staleInputs) {
repartitionInputs();
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> also.
sourceFile.clearCachedSource();
}
@Override
public InputId getInputId() {
return inputId;
}
@Override
public SourceFile getSourceFile() {
return sourceFile;
}
@Override
public void setSourceFile(SourceFile file) {
Preconditions.checkState(fileName.equals(file.getName()));
sourceFile = file;
}
private void parse(AbstractCompiler compiler) {
int startErrorCount = compiler.getErrorManager().getErrorCount();
try {
ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(),
compiler.getParserConfig(),
compiler.getDefaultErrorReporter(),
logger_);
root = result.ast;
compiler.setOldParseTree(sourceFile.getName(), result.oldAst);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName()));
}
if (root == null ||
// Most passes try to report as many errors as possible,
// so there may already be errors. We only care if there were
// errors in the code we just parsed.
(compiler.getErrorManager().getErrorCount() > startErrorCount && !compiler.isIdeMode())) {
// There was a parse error or IOException, so use a dummy block.
root = IR.script();
} else {
compiler.prepareAst(root);
}
// Set the source name so that the compiler passes can track
// the source file and module.
root.setStaticSourceFile(sourceFile);
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext();
declareVar(var);
scanVars(block);
return; // only one child to scan
case Token.SCRIPT:
inputId = n.getInputId();
Preconditions.checkNotNull(inputId);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name, Node n, CompilerInput input);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {}
}
/**
* Declares a variable.
*
* @param n The node corresponding to the variable name.
*/
private void declareVar(Node n) {
Preconditions.checkState(n.isName());
CompilerInput input = compiler.getInput(inputId);
String name = n.getString();
if (scope.isDeclared(name, false)
|| (scope.isLocal() && name.equals(ARGUMENTS))) {
redeclarationHandler.onRedeclaration(
scope, name, n, input);
} else {
scope.declare(name, n, null, input);
}
}
/**
* Generates an untyped global scope from the root of AST of compiler (which
* includes externs).
*
* @param compiler The compiler for which the scope is generated.
* @return The new untyped global scope generated as a result of this call.
*/
static Scope generateUntypedTopScope(AbstractCompiler compiler) {
return new SyntacticScopeCreator(compiler).createScope(compiler.getRoot(),
null);
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>, n);
} else if (n.isDelProp()) {
checkDelete(t, n);
} else if (n.isObjectLit()) {
checkObjectLiteral(t, n);
} else if (n.isLabel()) {
checkLabel(t, n);
}
}
/** Checks that the function is used legally. */
private void checkFunctionUse(NodeTraversal t, Node n) {
if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) {
t.report(n, BAD_FUNCTION_DECLARATION);
}
}
/**
* Determines if the given name is a declaration, which can be a declaration
* of a variable, function, or argument.
*/
private static boolean isDeclaration(Node n) {
switch (n.getParent().getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.CATCH:
return true;
case Token.PARAM_LIST:
return n.getParent().getParent().isFunction();
default:
return false;
}
}
/** Checks that the given name is used legally. */
private void checkNameUse(NodeTraversal t, Node n) {
Var v = t.getScope().getVar(n.getString());
if (v == null) {
// In particular, this prevents creating a global variable by assigning
// to it without a declaration.
if (!noVarCheck) {
t.report(n, UNKNOWN_VARIABLE, n.getString());
}
}
if (!noCajaChecks) {
if ("eval".equals(n.getString())) {
t.report(n, EVAL_USE);
} else if (n.getString().endsWith("__")) {
t.report(n, ILLEGAL_NAME);
}
}
}
/** Checks that an assignment is not to the "arguments" object. */
private void checkAssignment(NodeTraversal t, Node n) {
if (n.getFirstChild().isName()) {
if ("arguments".equals(n.getFirstChild().getString())) {
t.report(n, ARGUMENTS_ASSIGNMENT);
} else if ("eval".equals(n.getFirstChild().getString())) {
// Note that assignment to eval is already illegal because any use of
// that
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>traversal.makeError(node, MESSAGE_HAS_NO_DESCRIPTION,
messageKey));
}
JsMessageDefinition msgDefinition = new JsMessageDefinition(
node, msgNode, msgNodeParent);
processJsMessage(extractedMessage, msgDefinition);
}
/**
* Track a message for later retrieval.
*
* This is used for tracking duplicates, and for figuring out message
* fallback. Not all message types are trackable, because that would
* require a more sophisticated analysis. e.g.,
* function f(s) { s.MSG_UNNAMED_X = 'Some untrackable message'; }
*/
private void trackMessage(
NodeTraversal t, JsMessage message, String msgName,
Node msgNode, boolean isUnnamedMessage) {
if (!isUnnamedMessage) {
MessageLocation location = new MessageLocation(message, msgNode);
messageNames.put(msgName, location);
} else if (msgNode.isName()) {
Var var = t.getScope().getVar(msgName);
if (var != null) {
unnamedMessages.put(var, message);
}
}
}
/** Get a previously tracked message. */
private JsMessage getTrackedMessage(NodeTraversal t, String msgName) {
boolean isUnnamedMessage = isUnnamedMessageName(msgName);
if (!isUnnamedMessage) {
MessageLocation location = messageNames.get(msgName);
return location == null ? null : location.message;
} else {
Var var = t.getScope().getVar(msgName);
if (var != null) {
return unnamedMessages.get(var);
}
}
return null;
}
/**
* Checks if message already processed. If so - it generates 'message
* duplicated' compiler error.
*
* @param msgName the name of the message
* @param msgNode the node that represents JS message
*/
private void checkIfMessageDuplicated(String msgName, Node msgNode) {
if (messageNames.containsKey(msgName)) {
MessageLocation location = messageNames.get(msgName);
compiler.report(JSError.make(msgNode, MESSAGE_DUPLICATE_KEY,
msgName, location.messageNode.getSourceFileName(),
Integer.toString(location.message
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> be the highest type on the prototype
* chain of one of the members of the union.
*/
private T processProperty(
NodeTraversal t, Property prop, T type, T relatedType) {
type = typeSystem.restrictByNotNullOrUndefined(type);
if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) {
return null;
}
Iterable<T> alternatives = typeSystem.getTypeAlternatives(type);
if (alternatives != null) {
T firstType = relatedType;
for (T subType : alternatives) {
T lastType = processProperty(t, prop, subType, firstType);
if (lastType != null) {
firstType = firstType == null ? lastType : firstType;
}
}
return firstType;
} else {
T topType = typeSystem.getTypeWithProperty(prop.name, type);
if (typeSystem.isInvalidatingType(topType)) {
return null;
}
prop.addType(type, topType, relatedType);
return topType;
}
}
}
/** Renames all properties with references on more than one type. */
void renameProperties() {
int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0,
instancesSkipped = 0, singleTypeProps = 0;
Set<String> reported = Sets.newHashSet();
for (Property prop : properties.values()) {
if (prop.shouldRename()) {
Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name);
++propsRenamed;
prop.expandTypesToSkip();
for (Node node : prop.renameNodes) {
T rootType = prop.rootTypes.get(node);
if (prop.shouldRename(rootType)) {
String newName = propNames.get(rootType);
node.setString(newName);
compiler.reportCodeChange();
++instancesRenamed;
} else {
++instancesSkipped;
CheckLevel checkLevelForProp = propertiesToErrorFor.get(prop.name);
if (checkLevelForProp != null &&
checkLevelForProp != CheckLevel.OFF &&
!reported.contains(prop.name)) {
reported.add(prop
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.name);
compiler.report(JSError.make(
NodeUtil.getSourceName(node), node,
checkLevelForProp,
Warnings.INVALIDATION_ON_TYPE, prop.name,
rootType.toString(), ""));
}
}
}
} else {
if (prop.skipRenaming) {
++propsSkipped;
} else {
++singleTypeProps;
}
}
}
logger.fine("Renamed " + instancesRenamed + " instances of "
+ propsRenamed + " properties.");
logger.fine("Skipped renaming " + instancesSkipped + " invalidated "
+ "properties, " + propsSkipped + " instances of properties "
+ "that were skipped for specific types and " + singleTypeProps
+ " properties that were referenced from only one type.");
}
/**
* Chooses a name to use for renaming in each equivalence class and maps
* each type in that class to it.
*/
private Map<T, String> buildPropNames(UnionFind<T> types, String name) {
Map<T, String> names = Maps.newHashMap();
for (Set<T> set : types.allEquivalenceClasses()) {
checkState(!set.isEmpty());
String typeName = null;
for (T type : set) {
if (typeName == null || type.toString().compareTo(typeName) < 0) {
typeName = type.toString();
}
}
String newName;
if ("{...}".equals(typeName)) {
newName = name;
} else {
newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name;
}
for (T type : set) {
names.put(type, newName);
}
}
return names;
}
/** Returns a map from field name to types for which it will be renamed. */
Multimap<String, Collection<T>> getRenamedTypesForTesting() {
Multimap<String, Collection<T>> ret = HashMultimap.create();
for (Map.Entry<String, Property> entry : properties.entrySet()) {
Property prop = entry.getValue();
if (!prop.skipRenaming) {
for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) {
if (!c.isEmpty() &&
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> !NodeUtil.isFunctionDeclaration(n)) {
renamer.addDeclaredName(name);
}
nameStack.push(renamer);
}
break;
case Token.PARAM_LIST: {
Renamer renamer = nameStack.peek().forChildScope();
// Add the function parameters
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
String name = c.getString();
renamer.addDeclaredName(name);
}
// Add the function body declarations
Node functionBody = n.getNext();
findDeclaredNames(functionBody, null, renamer);
nameStack.push(renamer);
}
break;
case Token.CATCH:
{
Renamer renamer = nameStack.peek().forChildScope();
String name = n.getFirstChild().getString();
renamer.addDeclaredName(name);
nameStack.push(renamer);
}
break;
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
String newName = getReplacementName(n.getString());
if (newName != null) {
Renamer renamer = nameStack.peek();
if (renamer.stripConstIfReplaced()) {
// TODO(johnlenz): Do we need to do anything about the Javadoc?
n.removeProp(Node.IS_CONSTANT_NAME);
}
n.setString(newName);
t.getCompiler().reportCodeChange();
}
break;
case Token.FUNCTION:
// Remove the function body scope
nameStack.pop();
// Remove function recursive name (if any).
nameStack.pop();
break;
case Token.PARAM_LIST:
// Note: The parameters and function body variables live in the
// same scope, we introduce the scope when in the "shouldTraverse"
// visit of LP, but remove it when when we exit the function above.
break;
case Token.CATCH:
// Remove catch except name from the stack of names.
nameStack.pop();
break;
}
}
/**
* Walks the stack of name maps and finds the replacement name for the
* current scope
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.
*/
private String getReplacementName(String oldName) {
for (Renamer names : nameStack) {
String newName = names.getReplacementName(oldName);
if (newName != null) {
return newName;
}
}
return null;
}
/**
* Traverses the current scope and collects declared names. Does not
* decent into functions or add CATCH exceptions.
*/
private void findDeclaredNames(Node n, Node parent, Renamer renamer) {
// Do a shallow traversal, so don't traverse into function declarations,
// except for the name of the function itself.
if (parent == null
|| !parent.isFunction()
|| n == parent.getFirstChild()) {
if (NodeUtil.isVarDeclaration(n)) {
renamer.addDeclaredName(n.getString());
} else if (NodeUtil.isFunctionDeclaration(n)) {
Node nameNode = n.getFirstChild();
renamer.addDeclaredName(nameNode.getString());
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
findDeclaredNames(c, n, renamer);
}
}
}
/**
* Declared names renaming policy interface.
*/
interface Renamer {
/**
* Called when a declared name is found in the local current scope.
*/
void addDeclaredName(String name);
/**
* @return A replacement name, null if oldName is unknown or should not
* be replaced.
*/
String getReplacementName(String oldName);
/**
* @return Whether the constant-ness of a name should be removed.
*/
boolean stripConstIfReplaced();
/**
* @return A Renamer for a scope within the scope of the current Renamer.
*/
Renamer forChildScope();
}
/**
* Inverts the transformation by {@link ContextualRenamer}, when possible.
*/
static class ContextualRenameInverter
implements ScopedCallback, CompilerPass {
private final AbstractCompiler compiler;
// The set of names referenced in the current scope.
private Set<String> referencedNames = ImmutableSet.of();
// Stack reference sets.
private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>();
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>containsSeparator(name) && !getOrginalName(name).isEmpty()) {
String newName = findReplacementName(name);
referencedNames.remove(name);
// Adding a reference to the new name to prevent either the parent
// scopes or the current scope renaming another var to this new name.
referencedNames.add(newName);
List<Node> references = nameMap.get(name);
Preconditions.checkState(references != null);
for (Node n : references) {
Preconditions.checkState(n.isName());
n.setString(newName);
}
compiler.reportCodeChange();
nameMap.remove(name);
}
}
/**
* Find a name usable in the local scope.
*/
private String findReplacementName(String name) {
String original = getOrginalName(name);
String newName = original;
int i = 0;
while (!isValidName(newName)) {
newName = original +
ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++);
}
return newName;
}
/**
* @return Whether the name is valid to use in the local scope.
*/
private boolean isValidName(String name) {
if (TokenStream.isJSIdentifier(name) &&
!referencedNames.contains(name) &&
!name.equals(ARGUMENTS)) {
return true;
}
return false;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
if (t.inGlobalScope()) {
return;
}
if (NodeUtil.isReferenceName(node)) {
String name = node.getString();
// Add all referenced names to the set so it is possible to check for
// conflicts.
referencedNames.add(name);
// Store only references to candidate names in the node map.
if (containsSeparator(name)) {
addCandidateNameReference(name, node);
}
}
}
private void addCandidateNameReference(String name, Node n) {
List<Node> nodes = nameMap.get(name);
if (null == nodes) {
nodes = Lists.newLinkedList();
nameMap.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>put(name, nodes);
}
nodes.add(n);
}
}
/**
* Rename every locally name to be unique, the first encountered declaration
* (specifically global names) are left in their original form. Those that are
* renamed are made unique by giving them a unique suffix based on
* the number of declarations of the name.
*
* The root ContextualRenamer is assumed to be in GlobalScope.
*
* Used by the Normalize pass.
* @see Normalize
*/
static class ContextualRenamer implements Renamer {
private final Multiset<String> nameUsage;
private final Map<String, String> declarations = Maps.newHashMap();
private final boolean global;
static final String UNIQUE_ID_SEPARATOR = "$$";
ContextualRenamer() {
this.global = true;
nameUsage = HashMultiset.create();
}
/**
* Constructor for child scopes.
*/
private ContextualRenamer(Multiset<String> nameUsage) {
this.global = false;
this.nameUsage = nameUsage;
}
/**
* Create a ContextualRenamer
*/
@Override
public Renamer forChildScope() {
return new ContextualRenamer(nameUsage);
}
/**
* Adds a name to the map of names declared in this scope.
*/
@Override
public void addDeclaredName(String name) {
if (!name.equals(ARGUMENTS)) {
if (global) {
reserveName(name);
} else {
// It hasn't been declared locally yet, so increment the count.
if (!declarations.containsKey(name)) {
int id = incrementNameCount(name);
String newName = null;
if (id != 0) {
newName = getUniqueName(name, id);
}
declarations.put(name, newName);
}
}
}
}
@Override
public String getReplacementName(String oldName) {
return declarations.get(oldName);
}
/**
* Given a name and the associated id, create a new unique name.
*/
private String getUniqueName(String name, int id) {
return name + UNIQUE_ID_SEPARATOR + id;
}
private void reserveName(String name) {
nameUsage.setCount(name
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> // We have an assignment of the form "a.b = ...".
JSType lValueType = lValue.getJSType();
if (lValueType != null && lValueType.isNominalConstructor()) {
// If a.b is a constructor, then everything in this function
// belongs to the "a.b" type.
return (lValueType.toMaybeFunctionType()).getInstanceType();
} else {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
} else if (NodeUtil.isFunctionDeclaration(n) ||
parent.isName()) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isNominalConstructor()) {
return (type.toMaybeFunctionType()).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((ObjectType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
checkConstantProperty(t, n);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
case Token.FUNCTION:
check
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>FinalClassOverrides(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Node parent) {
JSType type = n.getJSType();
if (type != null) {
String deprecationInfo = getTypeDeprecationInfo(type);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_CLASS_REASON,
type.toString(), deprecationInfo));
} else {
compiler.report(
t.makeError(n, DEPRECATED_CLASS, type.toString()));
}
}
}
}
/**
* Checks the given NAME node to ensure that access restrictions are obeyed.
*/
private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking definitions or constructors.
if (parent.isFunction() || parent.isVar() ||
parent.isNew()) {
return;
}
Scope.Var var = t.getScope().getVar(n.getString());
JSDocInfo docInfo = var == null ? null : var.getJSDocInfo();
if (docInfo != null && docInfo.isDeprecated() &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (docInfo.getDeprecationReason() != null) {
compiler.report(
t.makeError(n, DEPRECATED_NAME_REASON, n.getString(),
docInfo.getDeprecationReason()));
} else {
compiler.report(
t.makeError(n, DEPRECATED_NAME, n.getString()));
}
}
}
/**
* Checks the given GETPROP node to ensure that access restrictions are
* obeyed.
*/
private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking constructors.
if (parent.isNew()) {
return;
}
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getJSType()));
String propertyName = n.getLastChild().getString();
if (objectType != null
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>) {
String deprecationInfo
= getPropertyDeprecationInfo(objectType, propertyName);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_PROP_REASON, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true),
deprecationInfo));
} else {
compiler.report(
t.makeError(n, DEPRECATED_PROP, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true)));
}
}
}
}
/**
* Determines whether the given name is visible in the current context.
* @param t The current traversal.
* @param name The name node.
*/
private void checkNameVisibility(NodeTraversal t, Node name, Node parent) {
Var var = t.getScope().getVar(name.getString());
if (var != null) {
JSDocInfo docInfo = var.getJSDocInfo();
if (docInfo != null) {
// If a name is private, make sure that we're in the same file.
Visibility visibility = docInfo.getVisibility();
if (visibility == Visibility.PRIVATE) {
StaticSourceFile varSrc = var.getSourceFile();
StaticSourceFile refSrc = name.getStaticSourceFile();
if (varSrc != null &&
refSrc != null &&
!varSrc.getName().equals(refSrc.getName())) {
if (docInfo.isConstructor() &&
isValidPrivateConstructorAccess(parent)) {
return;
}
compiler.report(
t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS,
name.getString(), varSrc.getName()));
}
}
}
}
}
/**
* Checks if a constructor is trying to override a final class.
*/
private void checkFinalClassOverrides(NodeTraversal t, Node fn, Node parent) {
JSType type = fn.getJSType().toMaybeFunctionType();
if (type != null && type.isConstructor()) {
JSType finalParentClass = getFinalParentClass(getClassOfMethod(fn, parent));
if (finalParentClass != null) {
compiler.report(
t.makeError(fn, EXTEND_FINAL_CLASS,
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>InSerial(final CompilerPass ... passes) {
return runInSerial(Lists.newArrayList(passes));
}
/** Create a compiler pass that runs the given passes in serial. */
private static CompilerPass runInSerial(
final Collection<CompilerPass> passes) {
return new CompilerPass() {
@Override public void process(Node externs, Node root) {
for (CompilerPass pass : passes) {
pass.process(externs, root);
}
}
};
}
@VisibleForTesting
static Map<String, Node> getAdditionalReplacements(
CompilerOptions options) {
Map<String, Node> additionalReplacements = Maps.newHashMap();
if (options.markAsCompiled || options.closurePass) {
additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode());
}
if (options.closurePass && options.locale != null) {
additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME,
IR.string(options.locale));
}
return additionalReplacements;
}
final PassFactory printNameReferenceGraph =
new PassFactory("printNameReferenceGraph", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
gc.process(externs, jsRoot);
String graphFileName = options.nameReferenceGraphPath;
try {
Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()),
new File(graphFileName),
Charsets.UTF_8);
} catch (IOException e) {
compiler.report(
JSError.make(
NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName));
}
}
};
}
};
final PassFactory printNameReferenceReport =
new PassFactory("printNameReferenceReport", true) {
@Override
protected CompilerPass create(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
NameReferenceGraphConstruction gc =
new NameReferenceGraphConstruction(compiler);
String reportFileName = options.nameReferenceReportPath;
try {
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
}
}
return typeNode;
} else {
return parseTypeExpression(token);
}
}
/**
* ParamTypeExpressionAnnotation :=
* '{' OptionalParameterType '}' |
* '{' TopLevelTypeExpression '}' |
* '{' '...' TopLevelTypeExpression '}'
*
* OptionalParameterType :=
* TopLevelTypeExpression '='
*/
private Node parseParamTypeExpressionAnnotation(JsDocToken token) {
Preconditions.checkArgument(token == JsDocToken.LC);
skipEOLs();
boolean restArg = false;
token = next();
if (token == JsDocToken.ELLIPSIS) {
token = next();
if (token == JsDocToken.RC) {
// EMPTY represents the UNKNOWN type in the Type AST.
return wrapNode(Token.ELLIPSIS, IR.empty());
}
restArg = true;
}
Node typeNode = parseTopLevelTypeExpression(token);
if (typeNode != null) {
skipEOLs();
if (restArg) {
typeNode = wrapNode(Token.ELLIPSIS, typeNode);
} else if (match(JsDocToken.EQUALS)) {
next();
skipEOLs();
typeNode = wrapNode(Token.EQUALS, typeNode);
}
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
}
/**
* TypeNameAnnotation := TypeName | '{' TypeName '}'
*/
private Node parseTypeNameAnnotation(JsDocToken token) {
if (token == JsDocToken.LC) {
skipEOLs();
Node typeNode = parseTypeName(next());
if (typeNode != null) {
skipEOLs();
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
} else {
return parseTypeName(token);
}
}
/**
* TopLevelTypeExpression := TypeExpression
* | TypeUnionList
*
* We made this rule up, for the sake of backwards compatibility.
*/
private Node parse
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>TopLevelTypeExpression(JsDocToken token) {
Node typeExpr = parseTypeExpression(token);
if (typeExpr != null) {
// top-level unions are allowed
if (match(JsDocToken.PIPE)) {
next();
if (match(JsDocToken.PIPE)) {
// We support double pipes for backwards-compatibility.
next();
}
skipEOLs();
token = next();
return parseUnionTypeWithAlternate(token, typeExpr);
}
}
return typeExpr;
}
/**
* TypeExpressionList := TopLevelTypeExpression
* | TopLevelTypeExpression ',' TypeExpressionList
*/
private Node parseTypeExpressionList(JsDocToken token) {
Node typeExpr = parseTopLevelTypeExpression(token);
if (typeExpr == null) {
return null;
}
Node typeList = IR.block();
typeList.addChildToBack(typeExpr);
while (match(JsDocToken.COMMA)) {
next();
skipEOLs();
typeExpr = parseTopLevelTypeExpression(next());
if (typeExpr == null) {
return null;
}
typeList.addChildToBack(typeExpr);
}
return typeList;
}
/**
* TypeExpression := BasicTypeExpression
* | '?' BasicTypeExpression
* | '!' BasicTypeExpression
* | BasicTypeExpression '?'
* | BasicTypeExpression '!'
* | '?'
*/
private Node parseTypeExpression(JsDocToken token) {
if (token == JsDocToken.QMARK) {
// A QMARK could mean that a type is nullable, or that it's unknown.
// We use look-ahead 1 to determine whether it's unknown. Otherwise,
// we assume it means nullable. There are 8 cases:
// {?} - right curly
// {?=} - equals
// {function(?, number)} - comma
// {function(number, ?)} - right paren
// {function(number, ...[?])} - right bracket
// {function(): ?|number} - pipe
// {Array.<?>} - greater than
// /** ? */ - EOC (inline types)
// I'm not a big fan of using look-ahead for this, but it makes
// the type language
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.
next();
// Move to the token after the colon and parse
// the type expression.
skipEOLs();
Node typeExpression = parseTypeExpression(next());
if (typeExpression == null) {
return null;
}
Node fieldType = newNode(Token.COLON);
fieldType.addChildToBack(fieldName);
fieldType.addChildToBack(typeExpression);
return fieldType;
}
/**
* FieldName := NameExpression | StringLiteral | NumberLiteral |
* ReservedIdentifier
*/
private Node parseFieldName(JsDocToken token) {
switch (token) {
case STRING:
String string = stream.getString();
return newStringNode(string);
default:
return null;
}
}
private Node wrapNode(int type, Node n) {
return n == null ? null :
new Node(type, n, stream.getLineno(),
stream.getCharno()).clonePropsFrom(templateNode);
}
private Node newNode(int type) {
return new Node(type, stream.getLineno(),
stream.getCharno()).clonePropsFrom(templateNode);
}
private Node newStringNode(String s) {
return newStringNode(s, stream.getLineno(), stream.getCharno());
}
private Node newStringNode(String s, int lineno, int charno) {
Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode);
n.setLength(s.length());
return n;
}
// This is similar to IRFactory.createTemplateNode to share common props
// e.g., source-name, between all nodes.
private Node createTemplateNode() {
// The Node type choice is arbitrary.
Node templateNode = IR.script();
templateNode.setStaticSourceFile(
this.associatedNode != null ?
this.associatedNode.getStaticSourceFile() :
null);
return templateNode;
}
private Node reportTypeSyntaxWarning(String warning) {
parser.addTypeWarning(warning, stream.getLineno(), stream.getCharno());
return null;
}
private Node reportGenericTypeSyntaxWarning() {
return reportTypeSyntaxWarning("msg.jsdoc.type.syntax");
}
private JsDocToken eatUntilEOLIfNotAnnotation() {
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> child.getNext()) {
if (!isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.OBJECTLIT:
// Return true only if all values are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child.getFirstChild(), includeFunctions)) {
return false;
}
}
return true;
case Token.FUNCTION:
return includeFunctions && !NodeUtil.isFunctionDeclaration(n);
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Binary operators are only valid if both children are valid.
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return isValidDefineValue(val.getFirstChild(), defines)
&& isValidDefineValue(val.getLastChild(), defines);
// Unary operators are valid if the child is valid.
case Token.NOT:
case Token.NEG:
case Token.POS:
return isValidDefineValue(val.getFirstChild(), defines);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> no children.
*
* @param block The node.
*/
static boolean isEmptyBlock(Node block) {
if (!block.isBlock()) {
return false;
}
for (Node n = block.getFirstChild(); n != null; n = n.getNext()) {
if (!n.isEmpty()) {
return false;
}
}
return true;
}
static boolean isSimpleOperator(Node n) {
return isSimpleOperatorType(n.getType());
}
/**
* A "simple" operator is one whose children are expressions,
* has no direct side-effects (unlike '+='), and has no
* conditional aspects (unlike '||').
*/
static boolean isSimpleOperatorType(int type) {
switch (type) {
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.COMMA:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GETELEM:
case Token.GETPROP:
case Token.GT:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.NOT:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.TYPEOF:
case Token.VOID:
case Token.POS:
case Token.NEG:
case Token.URSH:
return true;
default:
return false;
}
}
/**
* Creates an EXPR_RESULT.
*
* @param child The expression itself.
* @return Newly created EXPR node with the child as subexpression.
*/
static Node newExpr(Node child) {
return IR.exprResult(child).srcref(child);
}
/**
* Returns true if the node may create new mutable state, or change existing
* state.
*
* @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a>
*/
static boolean mayEffectMutableState(Node n) {
return may
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>(tryNode));
node.detachFromParent();
} else if (isTryCatchNodeContainer(node)) {
// The container node itself can't be removed, but the contained CATCH
// can if there is a 'finally' clause
Node tryNode = node.getParent();
Preconditions.checkState(NodeUtil.hasFinally(tryNode));
node.detachChildren();
} else if (node.isBlock()) {
// Simply empty the block. This maintains source location and
// "synthetic"-ness.
node.detachChildren();
} else if (isStatementBlock(parent)
|| isSwitchCase(node)) {
// A statement in a block can simply be removed.
parent.removeChild(node);
} else if (parent.isVar()) {
if (parent.hasMoreThanOneChild()) {
parent.removeChild(node);
} else {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// This would leave an empty VAR, remove the VAR itself.
removeChild(parent.getParent(), parent);
}
} else if (parent.isLabel()
&& node == parent.getLastChild()) {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// A LABEL without children can not be referred to, remove it.
removeChild(parent.getParent(), parent);
} else if (parent.isFor()
&& parent.getChildCount() == 4) {
// Only Token.FOR can have an Token.EMPTY other control structure
// need something for the condition. Others need to be replaced
// or the structure removed.
parent.replaceChild(node, IR.empty());
} else {
throw new IllegalStateException("Invalid attempt to remove node: " +
node.toString() + " of " + parent.toString());
}
}
/**
* Add a finally block if one does not exist.
*/
static void maybeAddFinally(Node tryNode) {
Preconditions.checkState(tryNode.isTry());
if (!NodeUtil.hasFinally(tryNode)) {
tryNode.addChildrenToBack(IR.block().srcref(tryNode));
}
}
/**
* Merge a block with its parent block.
* @return Whether the block was removed.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
*/
static boolean tryMergeBlock(Node block) {
Preconditions.checkState(block.isBlock());
Node parent = block.getParent();
// Try to remove the block if its parent is a block/script or if its
// parent is label and it has exactly one child.
if (isStatementBlock(parent)) {
Node previous = block;
while (block.hasChildren()) {
Node child = block.removeFirstChild();
parent.addChildAfter(child, previous);
previous = child;
}
parent.removeChild(block);
return true;
} else {
return false;
}
}
/**
* @param node A node
* @return Whether the call is a NEW or CALL node.
*/
static boolean isCallOrNew(Node node) {
return node.isCall() || node.isNew();
}
/**
* Return a BLOCK node for the given FUNCTION node.
*/
static Node getFunctionBody(Node fn) {
Preconditions.checkArgument(fn.isFunction());
return fn.getLastChild();
}
/**
* Is this node a function declaration? A function declaration is a function
* that has a name that is added to the current scope (i.e. a function that
* is not part of a expression; see {@link #isFunctionExpression}).
*/
static boolean isFunctionDeclaration(Node n) {
return n.isFunction() && isStatement(n);
}
/**
* Is this node a hoisted function declaration? A function declaration in the
* scope root is hoisted to the top of the scope.
* See {@link #isFunctionDeclaration}).
*/
static boolean isHoistedFunctionDeclaration(Node n) {
return isFunctionDeclaration(n)
&& (n.getParent().isScript()
|| n.getParent().getParent().isFunction());
}
/**
* Is a FUNCTION node an function expression? An function expression is one
* that has either no name or a name that is not added to the current scope.
*
* <p>Some examples of function expressions:
* <pre>
* (function () {})
* (function f() {})()
* [ function f() {} ]
* var f = function f() {};
* for (function f() {};;) {}
* </pre>
*
* <p>Some
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>MOD: return "%=";
case Token.VOID: return "void";
case Token.TYPEOF: return "typeof";
case Token.INSTANCEOF: return "instanceof";
default: return null;
}
}
/**
* Converts an operator's token value (see {@link Token}) to a string
* representation or fails.
*
* @param operator the operator's token value to convert
* @return the string representation
* @throws Error if the token value is not an operator
*/
static String opToStrNoFail(int operator) {
String res = opToStr(operator);
if (res == null) {
throw new Error("Unknown op " + operator + ": " +
Token.name(operator));
}
return res;
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = IR.var(
IR.name(nameNode.getString())
.srcref(nameNode))
.srcref(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.isBlock() ||
addingRoot.isScript());
Preconditions.checkState(addingRoot.getFirstChild() == null ||
!addingRoot.getFirstChild().isScript());
return addingRoot;
}
/**
* Creates a node representing a qualified name.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNode(
CodingConvention convention, String name) {
int endPos = name.indexOf('.');
if (endPos == -1) {
return newName(convention, name);
}
Node node;
String nodeName = name.substring(0, endPos);
if ("this".equals(nodeName)) {
node = IR.thisNode();
} else {
node = newName(convention, nodeName);
}
int startPos;
do {
startPos = endPos + 1;
endPos = name.indexOf('.', startPos);
String part = (endPos == -1
? name.substring(startPos)
: name.substring(startPos, endPos));
Node propNode = IR.string(part);
if (convention.isConstantKey(part)) {
propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
node = IR.getprop(node, propNode);
} while (endPos != -1);
return node;
}
/**
* Creates a node representing a qualified name.
*
* @param name A
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> qualified name (e.g. "foo" or "foo.bar.baz")
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNodeDeclaration(
CodingConvention convention, String name, Node value, JSDocInfo info) {
Node result;
Node nameNode = newQualifiedNameNode(convention, name);
if (nameNode.isName()) {
result = IR.var(nameNode, value);
result.setJSDocInfo(info);
} else if (value != null) {
result = IR.exprResult(IR.assign(nameNode, value));
result.getFirstChild().setJSDocInfo(info);
} else {
result = IR.exprResult(nameNode);
result.getFirstChild().setJSDocInfo(info);
}
return result;
}
/**
* Creates a node representing a qualified name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return A NAME or GETPROP node
*/
static Node newQualifiedNameNode(
CodingConvention convention, String name, Node basisNode,
String originalName) {
Node node = newQualifiedNameNode(convention, name);
setDebugInformation(node, basisNode, originalName);
return node;
}
/**
* Gets the root node of a qualified name. Must be either NAME or THIS.
*/
static Node getRootOfQualifiedName(Node qName) {
for (Node current = qName; true;
current = current.getFirstChild()) {
if (current.isName() || current.isThis()) {
return current;
}
Preconditions.checkState(current.isGetProp());
}
}
/**
* Sets the debug information (source file info and original name)
* on the given node.
*
* @param node The node on which to set the debug information.
* @param basisNode The basis node from which
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> to copy the source file info.
* @param originalName The original name of the node.
*/
static void setDebugInformation(Node node, Node basisNode,
String originalName) {
node.copyInformationFromForTree(basisNode);
node.putProp(Node.ORIGINALNAME_PROP, originalName);
}
private static Node newName(
CodingConvention convention, String name) {
Node nameNode = IR.name(name);
if (convention.isConstant(name)) {
nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
return nameNode;
}
/**
* Creates a new node representing an *existing* name, copying over the source
* location information from the basis node.
*
* @param name The name for the new NAME node.
* @param srcref The node that represents the name as currently found in
* the AST.
*
* @return The node created.
*/
static Node newName(CodingConvention convention, String name, Node srcref) {
return newName(convention, name).srcref(srcref);
}
/**
* Creates a new node representing an *existing* name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name The name for the new NAME node.
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return The node created.
*/
static Node newName(
CodingConvention convention, String name,
Node basisNode, String originalName) {
Node nameNode = newName(convention, name, basisNode);
nameNode.putProp(Node.ORIGINALNAME_PROP, originalName);
return nameNode;
}
/** Test if all characters in the string are in the Basic Latin (aka ASCII)
* character set - that they have UTF-16 values equal to or below 0x7f.
* This check can find which identifiers with Unicode characters need to be
* escaped in order to allow resulting files to be processed by non-Unicode
* aware UNIX tools and editors.
* *
*
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.isVar()) {
String name = n.getString();
if (!vars.containsKey(name)) {
vars.put(name, n);
}
}
}
}
}
/**
* Retrieves vars declared in the current node tree, excluding descent scopes.
*/
static Collection<Node> getVarsDeclaredInBranch(Node root) {
VarCollector collector = new VarCollector();
visitPreOrder(
root,
collector,
MATCH_NOT_FUNCTION);
return collector.vars.values();
}
/**
* @return {@code true} if the node an assignment to a prototype property of
* some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
return isExprAssign(n) &&
isPrototypeProperty(n.getFirstChild().getFirstChild());
}
/**
* @return Whether the node represents a qualified prototype property.
*/
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
return lhsString != null && lhsString.contains(".prototype.");
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (cur.isGetProp()) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode(Node srcReferenceNode) {
Node node = IR.voidNode(IR.number(0));
if (srcReferenceNode != null) {
node.copyInformationFromForTree(srcReferenceNode);
}
return node;
}
/**
* Create a VAR node containing the given name and initial value expression.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
*/
static Node newVarNode(String name, Node value) {
Node nodeName = IR.name(name);
if (value != null) {
Preconditions.checkState(value.getNext() == null);
nodeName.addChildToBack(value);
nodeName.srcref(value);
}
Node var = IR.var(nodeName).srcref(nodeName);
return var;
}
/**
* A predicate for matching name nodes with the specified node.
*/
private static class MatchNameNode implements Predicate<Node>{
final String name;
MatchNameNode(String name){
this.name = name;
}
@Override
public boolean apply(Node n) {
return n.isName() && n.getString().equals(name);
}
}
/**
* A predicate for matching nodes with the specified type.
*/
static class MatchNodeType implements Predicate<Node>{
final int type;
MatchNodeType(int type){
this.type = type;
}
@Override
public boolean apply(Node n) {
return n.getType() == type;
}
}
/**
* A predicate for matching var or function declarations.
*/
static class MatchDeclaration implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isFunctionDeclaration(n) || n.isVar();
}
}
/**
* A predicate for matching anything except function nodes.
*/
private static class MatchNotFunction implements Predicate<Node>{
@Override
public boolean apply(Node n) {
return !n.isFunction();
}
}
static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction();
/**
* A predicate for matching statements without exiting the current scope.
*/
static class MatchShallowStatement implements Predicate<Node>{
@Override
public boolean apply(Node n) {
Node parent = n.getParent();
return n.isBlock()
|| (!n.isFunction() && (parent == null
|| isControlStructure(parent)
|| isStatementBlock(parent)));
}
}
/**
* Finds the number of times a type is referenced within the node tree.
*/
static int getNodeTypeReferenceCount(
Node node, int type, Predicate<Node> traverseChildrenPred) {
return getCount(node, new MatchNodeType(
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> }
return sourceName;
}
/**
* @param n The node.
* @return The InputId property on the node or its ancestors.
*/
public static InputId getInputId(Node n) {
while (n != null && !n.isScript()) {
n = n.getParent();
}
return (n != null && n.isScript()) ? n.getInputId() : null;
}
/**
* A new CALL node with the "FREE_CALL" set based on call target.
*/
static Node newCallNode(Node callTarget, Node... parameters) {
boolean isFreeCall = !isGet(callTarget);
Node call = IR.call(callTarget);
call.putBooleanProp(Node.FREE_CALL, isFreeCall);
for (Node parameter : parameters) {
call.addChildToBack(parameter);
}
return call;
}
/**
* @return Whether the node is known to be a value that is not referenced
* elsewhere.
*/
static boolean evaluatesToLocalValue(Node value) {
return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse());
}
/**
* @param locals A predicate to apply to unknown local values.
* @return Whether the node is known to be a value that is not a reference
* outside the expression scope.
*/
static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) {
switch (value.getType()) {
case Token.CAST:
return evaluatesToLocalValue(value.getFirstChild(), locals);
case Token.ASSIGN:
// A result that is aliased by a non-local name, is the effectively the
// same as returning a non-local name, but this doesn't matter if the
// value is immutable.
return NodeUtil.isImmutableValue(value.getLastChild())
|| (locals.apply(value)
&& evaluatesToLocalValue(value.getLastChild(), locals));
case Token.COMMA:
return evaluatesToLocalValue(value.getLastChild(), locals);
case Token.AND:
case Token.OR:
return evaluatesToLocalValue(value.getFirstChild(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.HOOK:
return evaluatesToLocalValue(value.getFirstChild().getNext(), locals)
&& evaluates
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>SDocInfo();
}
}
return info;
}
/** Find the l-value that the given r-value is being assigned to. */
static Node getBestLValue(Node n) {
Node parent = n.getParent();
boolean isFunctionDeclaration = isFunctionDeclaration(n);
if (isFunctionDeclaration) {
return n.getFirstChild();
} else if (parent.isName()) {
return parent;
} else if (parent.isAssign()) {
return parent.getFirstChild();
} else if (isObjectLitKey(parent)) {
return parent;
} else if (
(parent.isHook() && parent.getFirstChild() != n) ||
parent.isOr() ||
parent.isAnd() ||
(parent.isComma() && parent.getFirstChild() != n)) {
return getBestLValue(parent);
} else if (parent.isCast()) {
return getBestLValue(parent);
}
return null;
}
/** Gets the r-value of a node returned by getBestLValue. */
static Node getRValueOfLValue(Node n) {
Node parent = n.getParent();
switch (parent.getType()) {
case Token.ASSIGN:
return n.getNext();
case Token.VAR:
return n.getFirstChild();
case Token.FUNCTION:
return parent;
}
return null;
}
/** Get the owner of the given l-value node. */
static Node getBestLValueOwner(@Nullable Node lValue) {
if (lValue == null || lValue.getParent() == null) {
return null;
}
if (isObjectLitKey(lValue)) {
return getBestLValue(lValue.getParent());
} else if (isGet(lValue)) {
return lValue.getFirstChild();
}
return null;
}
/** Get the name of the given l-value node. */
static String getBestLValueName(@Nullable Node lValue) {
if (lValue == null || lValue.getParent() == null) {
return null;
}
if (isObjectLitKey(lValue)) {
Node owner = getBestLValue(lValue.getParent());
if (owner != null) {
String ownerName = getBestLValueName(owner);
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>:
case Token.HOOK:
case Token.AND:
case Token.OR:
if (parent.getFirstChild() != n) {
return false;
}
// other ancestors may be conditional
continue inspect;
case Token.FOR:
if (NodeUtil.isForIn(parent)) {
if (parent.getChildAtIndex(1) != n) {
return false;
}
} else {
if (parent.getFirstChild() != n) {
return false;
}
}
// other ancestors may be conditional
continue inspect;
case Token.WHILE:
case Token.DO:
return false;
case Token.TRY:
// Consider all code under a try/catch to be conditionally executed.
if (!hasFinally(parent) || parent.getLastChild() != n) {
return false;
}
continue inspect;
case Token.CASE:
case Token.DEFAULT_CASE:
return false;
case Token.SCRIPT:
case Token.FUNCTION:
// Done, we've reached the scope root.
break inspect;
}
} while ((n = n.getParent()) != null);
return true;
}
/**
* @return An appropriate AST node for the boolean value.
*/
static Node booleanNode(boolean value) {
return value ? IR.trueNode() : IR.falseNode();
}
/**
* @return An appropriate AST node for the double value.
*/
static Node numberNode(double value, Node srcref) {
Node result;
if (Double.isNaN(value)) {
result = IR.name("NaN");
} else if (value == Double.POSITIVE_INFINITY) {
result = IR.name("Infinity");
} else if (value == Double.NEGATIVE_INFINITY) {
result = IR.neg(IR.name("Infinity"));
} else {
result = IR.number(value);
}
if (srcref != null) {
result.srcrefTree(srcref);
}
return result;
}
static boolean isNaN(Node n) {
if ((n.isName() && n.getString().equals("NaN")) ||
(n.getType() == Token.DIV &&
n.getFirstChild().isNumber() && n.getFirstChild().getDouble() == 0 &&
n.
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> maps m1
* and m2 is the map of the union of names with {@link JSType#getLeastSupertype}
* to meet the m1 type and m2 type.
*
* @see NodeTraversal
* @see DataFlowAnalysis
*
*/
public class Scope
implements StaticScope<JSType>, StaticSymbolTable<Scope.Var, Scope.Var> {
private final Map<String, Var> vars = new LinkedHashMap<String, Var>();
private final Scope parent;
private final int depth;
private final Node rootNode;
/** Whether this is a bottom scope for the purposes of type inference. */
private final boolean isBottom;
private Var arguments;
private static final Predicate<Var> DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES =
new Predicate<Var>() {
@Override public boolean apply(Var var) {
return var.getParentNode() != null &&
var.getType() == null && // no declared type
var.getParentNode().isVar() &&
!var.isExtern();
}
};
/** Stores info about a variable */
public static class Var
implements StaticSlot<JSType>, StaticReference<JSType> {
/** name */
final String name;
/** Var node */
final Node nameNode;
/**
* The variable's type.
*/
private JSType type;
/**
* Whether the variable's type has been inferred or is declared. An inferred
* type may change over time (as more code is discovered), whereas a
* declared type is a static contract that must be matched.
*/
private final boolean typeInferred;
/** Input source */
final CompilerInput input;
/**
* The index at which the var is declared. e..g if it's 0, it's the first
* declared variable in that scope
*/
final int index;
/** The enclosing scope */
final Scope scope;
/** @see #isMarkedEscaped */
private boolean markedEscaped = false;
/** @see #isMarkedAssignedExactlyOnce */
private boolean markedAssignedExactlyOnce = false;
/**
* Creates a variable.
*
* @param inferred whether its type is inferred (as opposed to declared)
*/
private Var(boolean inferred, String name, Node nameNode, JSType type,
Scope scope,
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> * @param type the variable's type
* @param input the input in which this variable is defined.
*/
Var declare(String name, Node nameNode, JSType type, CompilerInput input) {
return declare(name, nameNode, type, input, true);
}
/**
* Declares a variable.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
* @param inferred Whether this variable's type is inferred (as opposed
* to declared).
*/
Var declare(String name, Node nameNode,
JSType type, CompilerInput input, boolean inferred) {
Preconditions.checkState(name != null && name.length() > 0);
// Make sure that it's declared only once
Preconditions.checkState(vars.get(name) == null);
Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input);
vars.put(name, var);
return var;
}
/**
* Undeclares a variable, to be used when the compiler optimizes out
* a variable and removes it from the scope.
*/
void undeclare(Var var) {
Preconditions.checkState(var.scope == this);
Preconditions.checkState(vars.get(var.name) == var);
vars.remove(var.name);
}
@Override
public Var getSlot(String name) {
return getVar(name);
}
@Override
public Var getOwnSlot(String name) {
return vars.get(name);
}
/**
* Returns the variable, may be null
*/
public Var getVar(String name) {
Var var = vars.get(name);
if (var != null) {
return var;
} else if (parent != null) { // Recurse up the parent Scope
return parent.getVar(name);
} else {
return null;
}
}
/**
* Get a unique VAR object to represents "arguments" within this scope
*/
public Var getArgumentsVar() {
if (arguments == null) {
arguments = new Arguments(this);
}
return arguments;
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> /**
* Returns true if a variable is declared.
*/
public boolean isDeclared(String name, boolean recurse) {
Scope scope = this;
if (scope.vars.containsKey(name)) {
return true;
}
if (scope.parent != null && recurse) {
return scope.parent.isDeclared(name, recurse);
}
return false;
}
/**
* Return an iterator over all of the variables declared in this scope.
*/
public Iterator<Var> getVars() {
return vars.values().iterator();
}
/**
* Return an iterable over all of the variables declared in this scope.
*/
Iterable<Var> getVarIterable() {
return vars.values();
}
@Override
public Iterable<Var> getReferences(Var var) {
return ImmutableList.of(var);
}
@Override
public StaticScope<JSType> getScope(Var var) {
return var.scope;
}
@Override
public Iterable<Var> getAllSymbols() {
return Collections.unmodifiableCollection(vars.values());
}
/**
* Returns number of variables in this scope
*/
public int getVarCount() {
return vars.size();
}
/**
* Returns whether this is the global scope.
*/
public boolean isGlobal() {
return parent == null;
}
/**
* Returns whether this is a local scope (i.e. not the global scope).
*/
public boolean isLocal() {
return parent != null;
}
/**
* Gets all variables declared with "var" but without declared types attached.
*/
public Iterator<Var> getDeclarativelyUnboundVarsWithoutTypes() {
return Iterators.filter(
getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES);
}
static interface TypeResolver {
void resolveTypes();
}
private TypeResolver typeResolver;
/** Resolve all type references. Only used on typed scopes. */
void resolveTypes() {
if (typeResolver != null) {
typeResolver.resolveTypes();
typeResolver = null;
}
}
void setTypeResolver(TypeResolver resolver) {
this.typeResolver = resolver;
}
}
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction() {
return registerFunction != null;
}
String getName() {
return name;
}
String getExpectedTypeName() {
return expectedTypeName;
}
Node createDefaultValueNode() {
switch (this) {
case REGISTER_BOOLEAN:
return IR.falseNode();
case REGISTER_NUMBER:
return IR.number(0);
case REGISTER_STRING:
return IR.string("");
default:
throw new IllegalStateException();
}
}
}
// A map of function name -> TweakFunction.
private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP;
static {
TWEAK_FUNCTIONS_MAP = Maps.newHashMap();
for (TweakFunction func : TweakFunction.values()) {
TWEAK_FUNCTIONS_MAP.put(func.getName(), func);
}
}
ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks,
Map<String, Node> compilerDefaultValueOverrides) {
this.compiler = compiler;
this.stripTweaks = stripTweaks;
// Having the map sorted is required for the unit tests to be deterministic.
this.compilerDefaultValueOverrides = Maps.newTreeMap();
this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides);
}
@Override
public void process(Node externs, Node root) {
CollectTweaksResult result = collectTweaks(root);
applyCompilerDefaultValueOverrides(result.tweakInfos);
boolean changed = false;
if (stripTweaks) {
changed = stripAllCalls(result.tweakInfos);
} else if (!compilerDefaultValueOverrides.isEmpty()) {
changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls);
}
if (changed) {
compiler.reportCodeChange();
}
}
/**
* Passes the compiler default value overrides to the JS by replacing calls
* to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value;
*/
private boolean replaceGetCompilerOverridesCalls(
List<TweakFunctionCall> calls) {
for (TweakFunctionCall call : calls) {
Node callNode = call.callNode;
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>
Node objNode = createCompilerDefaultValueOverridesVarNode(callNode);
callNode.getParent().replaceChild(callNode, objNode);
}
return !calls.isEmpty();
}
/**
* Removes all CALL nodes in the given TweakInfos, replacing calls to getter
* functions with the tweak's default value.
*/
private boolean stripAllCalls(Map<String, TweakInfo> tweakInfos) {
for (TweakInfo tweakInfo : tweakInfos.values()) {
boolean isRegistered = tweakInfo.isRegistered();
for (TweakFunctionCall functionCall : tweakInfo.functionCalls) {
Node callNode = functionCall.callNode;
Node parent = callNode.getParent();
if (functionCall.tweakFunc.isGetterFunction()) {
Node newValue;
if (isRegistered) {
newValue = tweakInfo.getDefaultValueNode().cloneNode();
} else {
// When we find a getter of an unregistered tweak, there has
// already been a warning about it, so now just use a default
// value when stripping.
TweakFunction registerFunction =
functionCall.tweakFunc.registerFunction;
newValue = registerFunction.createDefaultValueNode();
}
parent.replaceChild(callNode, newValue);
} else {
Node voidZeroNode = IR.voidNode(IR.number(0).srcref(callNode))
.srcref(callNode);
parent.replaceChild(callNode, voidZeroNode);
}
}
}
return !tweakInfos.isEmpty();
}
/**
* Creates a JS object that holds a map of tweakId -> default value override.
*/
private Node createCompilerDefaultValueOverridesVarNode(
Node sourceInformationNode) {
Node objNode = IR.objectlit().srcref(sourceInformationNode);
for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) {
Node objKeyNode = IR.stringKey(entry.getKey())
.copyInformationFrom(sourceInformationNode);
Node objValueNode = entry.getValue().cloneNode()
.copyInformationFrom(sourceInformationNode);
objKeyNode.addChildToBack(objValueNode);
objNode.addChildToBack(objKeyNode);
}
return objNode;
}
/** Sets the default values of tweaks based on compiler options. */
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>Builder inferParameterTypes(JSDocInfo info) {
// Create a fake args parent.
Node lp = IR.paramList();
for (String name : info.getParameterNames()) {
lp.addChildToBack(IR.name(name));
}
return inferParameterTypes(lp, info);
}
/**
* Infer the parameter types from the list of argument names and
* the doc info.
*/
FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent,
@Nullable JSDocInfo info) {
if (argsParent == null) {
if (info == null) {
return this;
} else {
return inferParameterTypes(info);
}
}
// arguments
Node oldParameterType = null;
if (parametersNode != null) {
oldParameterType = parametersNode.getFirstChild();
}
FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry);
boolean warnedAboutArgList = false;
Set<String> allJsDocParams = (info == null) ?
Sets.<String>newHashSet() :
Sets.newHashSet(info.getParameterNames());
boolean isVarArgs = false;
for (Node arg : argsParent.children()) {
String argumentName = arg.getString();
allJsDocParams.remove(argumentName);
// type from JSDocInfo
JSType parameterType = null;
boolean isOptionalParam = isOptionalParameter(arg, info);
isVarArgs = isVarArgsParameter(arg, info);
if (info != null && info.hasParameterType(argumentName)) {
parameterType =
info.getParameterType(argumentName).evaluate(scope, typeRegistry);
} else if (arg.getJSDocInfo() != null && arg.getJSDocInfo().hasType()) {
parameterType =
arg.getJSDocInfo().getType().evaluate(scope, typeRegistry);
} else if (oldParameterType != null &&
oldParameterType.getJSType() != null) {
parameterType = oldParameterType.getJSType();
isOptionalParam = oldParameterType.isOptionalArg();
isVarArgs = oldParameterType.isVarArgs();
} else {
parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE);
}
warnedAboutArgList |= addParameter(
builder, parameterType, warned
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> here we only wipe that part off!
// Remove all variables that were previously declared in this scripts.
// First find all vars to remove then remove them because of iterator!
Iterator<Var> varIter = globalScope.getVars();
List<Var> varsToRemove = Lists.newArrayList();
while (varIter.hasNext()) {
Var oldVar = varIter.next();
if (scriptName.equals(oldVar.getInputName())) {
varsToRemove.add(oldVar);
}
}
for (Var var : varsToRemove) {
globalScope.undeclare(var);
globalScope.getTypeOfThis().toObjectType().removeProperty(var.getName());
}
// Now re-traverse the given script.
GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope);
NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder);
}
/**
* Create the outermost scope. This scope contains native binding such as
* {@code Object}, {@code Date}, etc.
*/
@VisibleForTesting
Scope createInitialScope(Node root) {
NodeTraversal.traverse(
compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry));
Scope s = Scope.createGlobalScope(root);
declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE);
declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, DATE_FUNCTION_TYPE);
declareNativeFunctionType(s, ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE);
declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE);
declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE);
declareNativeValueType(s, "undefined", VOID_TYPE);
// There is no longer a
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>Traverse(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
if (n.isFunction() ||
n.isScript()) {
Preconditions.checkNotNull(inputId);
sourceName = NodeUtil.getSourceName(n);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || !parent.isFunction() ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
if (NodeUtil.isHoistedFunctionDeclaration(child)) {
defineFunctionLiteral(child);
}
}
}
}
return descend;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
attachLiteralTypes(n);
switch (n.getType()) {
case Token.CALL:
checkForClassDefiningCalls(t, n);
checkForCallingConventionDefiningCalls(n, delegateCallingConventions);
break;
case Token.FUNCTION:
if (t.getInput() == null || !t.getInput().isExtern()) {
nonExternFunctions.add(n);
}
// Hoisted functions are handled during pre-traversal.
if (!NodeUtil.isHoistedFunctionDeclaration(n)) {
defineFunctionLiteral(n);
}
break;
case Token.ASSIGN:
// Handle initialization of properties.
Node firstChild = n.getFirstChild();
if (firstChild.isGetProp() &&
firstChild.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(),
firstChild, n, firstChild.getNext());
}
break;
case Token.CATCH:
defineCatch(n);
break;
case Token.VAR:
defineVar(n);
break;
case Token.GETPROP:
// Handle stubbed properties.
if (parent.isExprResult() &&
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>DeferredType(n, functionType);
// Declare this symbol in the current scope iff it's a function
// declaration. Otherwise, the declaration will happen in other
// code paths.
if (NodeUtil.isFunctionDeclaration(n)) {
defineSlot(n.getFirstChild(), n, functionType);
}
}
/**
* Defines a variable based on the {@link Token#NAME} node passed.
* @param name The {@link Token#NAME} node.
* @param var The parent of the {@code name} node, which must be a
* {@link Token#VAR} node.
* @param info the {@link JSDocInfo} information relating to this
* {@code name} node.
*/
private void defineName(Node name, Node var, JSDocInfo info) {
Node value = name.getFirstChild();
// variable's type
JSType type = getDeclaredType(info, name, value);
if (type == null) {
// The variable's type will be inferred.
type = name.isFromExterns() ? unknownType : null;
}
defineSlot(name, var, type);
}
/**
* If a variable is assigned a function literal in the global scope,
* make that a declared type (even if there's no doc info).
* There's only one exception to this rule:
* if the return type is inferred, and we're in a local
* scope, we should assume the whole function is inferred.
*/
private boolean shouldUseFunctionLiteralType(
FunctionType type, JSDocInfo info, Node lValue) {
if (info != null) {
return true;
}
if (lValue != null &&
NodeUtil.isObjectLitKey(lValue)) {
return false;
}
return scope.isGlobal() || !type.isReturnTypeInferred();
}
/**
* Creates a new function type, based on the given nodes.
*
* This handles two cases that are semantically very different, but
* are not mutually exclusive:
* - A function literal that needs a type attached to it.
* - An assignment expression with function-type info in the JsDoc.
*
* All parameters are optional, and we will do the best we can to create
* a function type
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS>.
*
* This function will always create a function type, so only call it if
* you're sure that's what you want.
*
* @param rValue The function node.
* @param name the function's name
* @param info the {@link JSDocInfo} attached to the function definition
* @param lvalueNode The node where this function is being
* assigned. For example, {@code A.prototype.foo = ...} would be used to
* determine that this function is a method of A.prototype. May be
* null to indicate that this is not being assigned to a qualified name.
*/
private FunctionType createFunctionTypeFromNodes(
@Nullable Node rValue,
@Nullable String name,
@Nullable JSDocInfo info,
@Nullable Node lvalueNode) {
FunctionType functionType = null;
// Global ctor aliases should be registered with the type registry.
if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) {
Var var = scope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() != null &&
var.getType().isFunctionType()) {
FunctionType aliasedType = var.getType().toMaybeFunctionType();
if ((aliasedType.isConstructor() || aliasedType.isInterface()) &&
!aliasedType.isNativeObjectType()) {
functionType = aliasedType;
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, functionType.getInstanceType());
}
}
}
}
if (functionType == null) {
Node errorRoot = rValue == null ? lvalueNode : rValue;
boolean isFnLiteral =
rValue != null && rValue.isFunction();
Node fnRoot = isFnLiteral ? rValue : null;
Node parametersNode = isFnLiteral ?
rValue.getFirstChild().getNext() : null;
if (info != null && info.hasType()) {
JSType type = info.getType().evaluate(scope, typeRegistry);
// Known to be not null since we have the FUNCTION token there.
type = type.restrictByNotNullOrUndefined();
if (type.isFunctionType()) {
functionType = type.toMaybeFunctionType();
functionType.setJSD
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> This handles two cases that are semantically very different, but
* are not mutually exclusive:
* - An object literal that needs an enum type attached to it.
* - An assignment expression with an enum tag in the JsDoc.
*
* This function will always create an enum type, so only call it if
* you're sure that's what you want.
*
* @param rValue The node of the enum.
* @param name The enum's name
* @param info The {@link JSDocInfo} attached to the enum definition.
* @param lValueNode The node where this function is being
* assigned.
*/
private EnumType createEnumTypeFromNodes(Node rValue, String name,
JSDocInfo info, Node lValueNode) {
Preconditions.checkNotNull(info);
Preconditions.checkState(info.hasEnumParameterType());
EnumType enumType = null;
if (rValue != null && rValue.isQualifiedName()) {
// Handle an aliased enum.
Var var = scope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() instanceof EnumType) {
enumType = (EnumType) var.getType();
}
}
if (enumType == null) {
JSType elementsType =
info.getEnumParameterType().evaluate(scope, typeRegistry);
enumType = typeRegistry.createEnumType(name, rValue, elementsType);
if (rValue != null && rValue.isObjectLit()) {
// collect enum elements
Node key = rValue.getFirstChild();
while (key != null) {
String keyName = NodeUtil.getStringValue(key);
if (keyName == null) {
// GET and SET don't have a String value;
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else if (!codingConvention.isValidEnumKey(keyName)) {
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else {
enumType.defineElement(keyName, key);
}
key = key.getNext();
}
}
}
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name,
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> same place.
prototypeSlot.setNode(n);
String prototypeName = variableName + ".prototype";
// There are some rare cases where the prototype will already
// be declared. See TypedScopeCreatorTest#testBogusPrototypeInit.
// Fortunately, other warnings will complain if this happens.
Var prototypeVar = scopeToDeclareIn.getVar(prototypeName);
if (prototypeVar != null && prototypeVar.scope == scopeToDeclareIn) {
scopeToDeclareIn.undeclare(prototypeVar);
}
scopeToDeclareIn.declare(prototypeName,
n, prototypeSlot.getType(), input,
/* declared iff there's an explicit supertype */
superClassCtor == null ||
superClassCtor.getInstanceType().isEquivalentTo(
getNativeType(OBJECT_TYPE)));
// Make sure the variable is initialized to something if
// it constructs itself.
if (newVar.getInitialValue() == null &&
!n.isFromExterns()) {
compiler.report(
JSError.make(sourceName, n,
fnType.isConstructor() ?
CTOR_INITIALIZER : IFACE_INITIALIZER,
variableName));
}
}
/**
* Check if the given node is a property of a name in the global scope.
*/
private boolean isQnameRootedInGlobalScope(Node n) {
Scope scope = getQnameRootScope(n);
return scope != null && scope.isGlobal();
}
/**
* Return the scope for the name of the given node.
*/
private Scope getQnameRootScope(Node n) {
Node root = NodeUtil.getRootOfQualifiedName(n);
if (root.isName()) {
Var var = scope.getVar(root.getString());
if (var != null) {
return var.getScope();
}
}
return null;
}
/**
* Look for a type declaration on a property assignment
* (in an ASSIGN or an object literal key).
* @param info The doc info for this property.
* @param lValue The l-value node.
* @param rValue The node that {@code n} is being initialized to,
* or {@code null} if this is a stub declaration.
*/
private JSType getDeclaredType(JSDocInfo
Closure, 110
<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
Node parent = n.getParent();
boolean isVar = parent.isVar();
<CHANGES>
<CHANGEE>
if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) {
recordAlias(v);
} else if (v.isBleedingFunction()) {
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
} else if (parent.getType() == Token.LP) {
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
<CHANGES>
} else if (isVar) {
<CHANGEE>
Node grandparent = parent.getParent();
<CHANGES>
Node value = n.hasChildren() ?
v.getInitialValue().detachFromParent() :
<CHANGEE>
null;
<CHANGES>
Node varNode = parent;
<CHANGEE>
String name = n.getString();
int nameCount = scopedAliasNames.count(name);
scopedAliasNames.add(name);
String globalName =
"$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount));
compiler.ensureLibraryInjected("base");
// First, we need to free up the function expression (EXPR)
// to be used in another expression.
<CHANGES>
<CHANGEE>
// Replace "function NAME() { ... }" with "var NAME;".
<CHANGES>
<CHANGEE>
// We can't keep the local name on the function expression,
// because IE is buggy and will leak the name into the global
// scope. This is covered in more detail here:
// http://wiki.ecmascript.org/lib/<SCANS> = var.getScope();
if (ownerScope.isLocal()) {
data.get(ownerScope.getRootNode()).recordAssignedName(name);
}
if (scope != ownerScope && ownerScope.isLocal()) {
data.get(ownerScope.getRootNode()).recordEscapedVarName(name);
}
}
} else if (n.isGetProp() && n.isUnscopedQualifiedName() &&
NodeUtil.isLValue(n)) {
String name = NodeUtil.getRootOfQualifiedName(n).getString();
Scope scope = t.getScope();
Var var = scope.getVar(name);
if (var != null) {
Scope ownerScope = var.getScope();
if (scope != ownerScope && ownerScope.isLocal()) {
data.get(ownerScope.getRootNode())
.recordEscapedQualifiedName(n.getQualifiedName());
}
}
}
}
}
private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) {
if (n == null) {
return null;
}
// Sometimes this will return null in things like
// NameReferenceGraphConstruction that build partial scopes.
return functionAnalysisResults.get(n);
}
}